diff options
Diffstat (limited to 'src/core')
67 files changed, 3036 insertions, 1170 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 78986deb5..64fdf38cd 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -156,6 +156,8 @@ add_library(core STATIC hle/service/am/omm.h hle/service/am/spsm.cpp hle/service/am/spsm.h + hle/service/am/tcap.cpp + hle/service/am/tcap.h hle/service/aoc/aoc_u.cpp hle/service/aoc/aoc_u.h hle/service/apm/apm.cpp @@ -236,6 +238,24 @@ add_library(core STATIC hle/service/hid/irs.h hle/service/hid/xcd.cpp hle/service/hid/xcd.h + hle/service/hid/controllers/controller_base.cpp + hle/service/hid/controllers/controller_base.h + hle/service/hid/controllers/debug_pad.cpp + hle/service/hid/controllers/debug_pad.h + hle/service/hid/controllers/gesture.cpp + hle/service/hid/controllers/gesture.h + hle/service/hid/controllers/keyboard.cpp + hle/service/hid/controllers/keyboard.h + hle/service/hid/controllers/mouse.cpp + hle/service/hid/controllers/mouse.h + hle/service/hid/controllers/npad.cpp + hle/service/hid/controllers/npad.h + hle/service/hid/controllers/stubbed.cpp + hle/service/hid/controllers/stubbed.h + hle/service/hid/controllers/touchscreen.cpp + hle/service/hid/controllers/touchscreen.h + hle/service/hid/controllers/xpad.cpp + hle/service/hid/controllers/xpad.h hle/service/lbl/lbl.cpp hle/service/lbl/lbl.h hle/service/ldn/ldn.cpp @@ -262,6 +282,8 @@ add_library(core STATIC hle/service/nifm/nifm.h hle/service/nim/nim.cpp hle/service/nim/nim.h + hle/service/npns/npns.cpp + hle/service/npns/npns.h hle/service/ns/ns.cpp hle/service/ns/ns.h hle/service/ns/pl_u.cpp @@ -309,6 +331,8 @@ add_library(core STATIC hle/service/prepo/prepo.h hle/service/psc/psc.cpp hle/service/psc/psc.h + hle/service/ptm/psm.cpp + hle/service/ptm/psm.h hle/service/service.cpp hle/service/service.h hle/service/set/set.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index 3c57a62ec..7cb86ed92 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -375,8 +375,7 @@ const Kernel::Process* System::CurrentProcess() const { } ARM_Interface& System::ArmInterface(std::size_t core_index) { - ASSERT(core_index < NUM_CPU_CORES); - return impl->cpu_cores[core_index]->ArmInterface(); + return CpuCore(core_index).ArmInterface(); } Cpu& System::CpuCore(std::size_t core_index) { diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp index 25cee1f3a..ed0775444 100644 --- a/src/core/crypto/partition_data_manager.cpp +++ b/src/core/crypto/partition_data_manager.cpp @@ -516,7 +516,8 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa out.insert(out.end(), rodata.begin(), rodata.end()); out.insert(out.end(), data.begin(), data.end()); - offset += sizeof(KIPHeader) + out.size(); + offset += sizeof(KIPHeader) + kip.sections[0].size_compressed + + kip.sections[1].size_compressed + kip.sections[2].size_compressed; if (name == "FS") package2_fs[static_cast<size_t>(type)] = std::move(out); diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index 8f5142a07..ecdd7505b 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp @@ -122,14 +122,16 @@ u64 XCI::GetProgramTitleID() const { return secure_partition->GetProgramTitleID(); } -std::shared_ptr<NCA> XCI::GetProgramNCA() const { - return program; +bool XCI::HasProgramNCA() const { + return program != nullptr; } VirtualFile XCI::GetProgramNCAFile() const { - if (GetProgramNCA() == nullptr) + if (!HasProgramNCA()) { return nullptr; - return GetProgramNCA()->GetBaseFile(); + } + + return program->GetBaseFile(); } const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const { diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index ce514dfa0..48cbef666 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h @@ -80,7 +80,7 @@ public: u64 GetProgramTitleID() const; - std::shared_ptr<NCA> GetProgramNCA() const; + bool HasProgramNCA() const; VirtualFile GetProgramNCAFile() const; const std::vector<std::shared_ptr<NCA>>& GetNCAs() const; std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const; diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 6dcec7816..6c356d85d 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -97,11 +97,288 @@ union NCASectionHeader { }; static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size."); -bool IsValidNCA(const NCAHeader& header) { +static bool IsValidNCA(const NCAHeader& header) { // TODO(DarkLordZach): Add NCA2/NCA0 support. return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); } +NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset) + : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) { + if (file == nullptr) { + status = Loader::ResultStatus::ErrorNullFile; + return; + } + + if (sizeof(NCAHeader) != file->ReadObject(&header)) { + LOG_ERROR(Loader, "File reader errored out during header read."); + status = Loader::ResultStatus::ErrorBadNCAHeader; + return; + } + + if (!HandlePotentialHeaderDecryption()) { + return; + } + + has_rights_id = std::any_of(header.rights_id.begin(), header.rights_id.end(), + [](char c) { return c != '\0'; }); + + const std::vector<NCASectionHeader> sections = ReadSectionHeaders(); + is_update = std::any_of(sections.begin(), sections.end(), [](const NCASectionHeader& header) { + return header.raw.header.crypto_type == NCASectionCryptoType::BKTR; + }); + + if (!ReadSections(sections, bktr_base_ivfc_offset)) { + return; + } + + status = Loader::ResultStatus::Success; +} + +NCA::~NCA() = default; + +bool NCA::CheckSupportedNCA(const NCAHeader& nca_header) { + if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) { + status = Loader::ResultStatus::ErrorNCA2; + return false; + } + + if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) { + status = Loader::ResultStatus::ErrorNCA0; + return false; + } + + return true; +} + +bool NCA::HandlePotentialHeaderDecryption() { + if (IsValidNCA(header)) { + return true; + } + + if (!CheckSupportedNCA(header)) { + return false; + } + + NCAHeader dec_header{}; + Core::Crypto::AESCipher<Core::Crypto::Key256> cipher( + keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS); + cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200, + Core::Crypto::Op::Decrypt); + if (IsValidNCA(dec_header)) { + header = dec_header; + encrypted = true; + } else { + if (!CheckSupportedNCA(dec_header)) { + return false; + } + + if (keys.HasKey(Core::Crypto::S256KeyType::Header)) { + status = Loader::ResultStatus::ErrorIncorrectHeaderKey; + } else { + status = Loader::ResultStatus::ErrorMissingHeaderKey; + } + return false; + } + + return true; +} + +std::vector<NCASectionHeader> NCA::ReadSectionHeaders() const { + const std::ptrdiff_t number_sections = + std::count_if(std::begin(header.section_tables), std::end(header.section_tables), + [](NCASectionTableEntry entry) { return entry.media_offset > 0; }); + + std::vector<NCASectionHeader> sections(number_sections); + const auto length_sections = SECTION_HEADER_SIZE * number_sections; + + if (encrypted) { + auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET); + Core::Crypto::AESCipher<Core::Crypto::Key256> cipher( + keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS); + cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE, + Core::Crypto::Op::Decrypt); + } else { + file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET); + } + + return sections; +} + +bool NCA::ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_base_ivfc_offset) { + for (std::size_t i = 0; i < sections.size(); ++i) { + const auto& section = sections[i]; + + if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) { + if (!ReadRomFSSection(section, header.section_tables[i], bktr_base_ivfc_offset)) { + return false; + } + } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) { + if (!ReadPFS0Section(section, header.section_tables[i])) { + return false; + } + } + } + + return true; +} + +bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry, + u64 bktr_base_ivfc_offset) { + const std::size_t base_offset = entry.media_offset * MEDIA_OFFSET_MULTIPLIER; + ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; + const std::size_t romfs_offset = base_offset + ivfc_offset; + const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size; + auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset); + auto dec = Decrypt(section, raw, romfs_offset); + + if (dec == nullptr) { + if (status != Loader::ResultStatus::Success) + return false; + if (has_rights_id) + status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek; + else + status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey; + return false; + } + + if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) { + if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') || + section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) { + status = Loader::ResultStatus::ErrorBadBKTRHeader; + return false; + } + + if (section.bktr.relocation.offset + section.bktr.relocation.size != + section.bktr.subsection.offset) { + status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation; + return false; + } + + const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset); + if (section.bktr.subsection.offset + section.bktr.subsection.size != size) { + status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd; + return false; + } + + const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; + RelocationBlock relocation_block{}; + if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) != + sizeof(RelocationBlock)) { + status = Loader::ResultStatus::ErrorBadRelocationBlock; + return false; + } + SubsectionBlock subsection_block{}; + if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) != + sizeof(RelocationBlock)) { + status = Loader::ResultStatus::ErrorBadSubsectionBlock; + return false; + } + + std::vector<RelocationBucketRaw> relocation_buckets_raw( + (section.bktr.relocation.size - sizeof(RelocationBlock)) / sizeof(RelocationBucketRaw)); + if (dec->ReadBytes(relocation_buckets_raw.data(), + section.bktr.relocation.size - sizeof(RelocationBlock), + section.bktr.relocation.offset + sizeof(RelocationBlock) - offset) != + section.bktr.relocation.size - sizeof(RelocationBlock)) { + status = Loader::ResultStatus::ErrorBadRelocationBuckets; + return false; + } + + std::vector<SubsectionBucketRaw> subsection_buckets_raw( + (section.bktr.subsection.size - sizeof(SubsectionBlock)) / sizeof(SubsectionBucketRaw)); + if (dec->ReadBytes(subsection_buckets_raw.data(), + section.bktr.subsection.size - sizeof(SubsectionBlock), + section.bktr.subsection.offset + sizeof(SubsectionBlock) - offset) != + section.bktr.subsection.size - sizeof(SubsectionBlock)) { + status = Loader::ResultStatus::ErrorBadSubsectionBuckets; + return false; + } + + std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size()); + std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(), + relocation_buckets.begin(), &ConvertRelocationBucketRaw); + std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size()); + std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(), + subsection_buckets.begin(), &ConvertSubsectionBucketRaw); + + u32 ctr_low; + std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low)); + subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low}); + subsection_buckets.back().entries.push_back({size, {0}, 0}); + + boost::optional<Core::Crypto::Key128> key = boost::none; + if (encrypted) { + if (has_rights_id) { + status = Loader::ResultStatus::Success; + key = GetTitlekey(); + if (key == boost::none) { + status = Loader::ResultStatus::ErrorMissingTitlekey; + return false; + } + } else { + key = GetKeyAreaKey(NCASectionCryptoType::BKTR); + if (key == boost::none) { + status = Loader::ResultStatus::ErrorMissingKeyAreaKey; + return false; + } + } + } + + if (bktr_base_romfs == nullptr) { + status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS; + return false; + } + + auto bktr = std::make_shared<BKTR>( + bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset), + relocation_block, relocation_buckets, subsection_block, subsection_buckets, encrypted, + encrypted ? key.get() : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset, + section.raw.section_ctr); + + // BKTR applies to entire IVFC, so make an offset version to level 6 + files.push_back(std::make_shared<OffsetVfsFile>( + bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset)); + } else { + files.push_back(std::move(dec)); + } + + romfs = files.back(); + return true; +} + +bool NCA::ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry) { + const u64 offset = (static_cast<u64>(entry.media_offset) * MEDIA_OFFSET_MULTIPLIER) + + section.pfs0.pfs0_header_offset; + const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset); + + auto dec = Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset); + if (dec != nullptr) { + auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec)); + + if (npfs->GetStatus() == Loader::ResultStatus::Success) { + dirs.push_back(std::move(npfs)); + if (IsDirectoryExeFS(dirs.back())) + exefs = dirs.back(); + } else { + if (has_rights_id) + status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek; + else + status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey; + return false; + } + } else { + if (status != Loader::ResultStatus::Success) + return false; + if (has_rights_id) + status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek; + else + status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey; + return false; + } + + return true; +} + u8 NCA::GetCryptoRevision() const { u8 master_key_id = header.crypto_type; if (header.crypto_type_2 > master_key_id) @@ -167,7 +444,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() { return titlekey; } -VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) { +VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 starting_offset) { if (!encrypted) return in; @@ -215,256 +492,6 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting } } -NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset) - : file(std::move(file_)), - bktr_base_romfs(bktr_base_romfs_ ? std::move(bktr_base_romfs_) : nullptr) { - status = Loader::ResultStatus::Success; - - if (file == nullptr) { - status = Loader::ResultStatus::ErrorNullFile; - return; - } - - if (sizeof(NCAHeader) != file->ReadObject(&header)) { - LOG_ERROR(Loader, "File reader errored out during header read."); - status = Loader::ResultStatus::ErrorBadNCAHeader; - return; - } - - encrypted = false; - - if (!IsValidNCA(header)) { - if (header.magic == Common::MakeMagic('N', 'C', 'A', '2')) { - status = Loader::ResultStatus::ErrorNCA2; - return; - } - if (header.magic == Common::MakeMagic('N', 'C', 'A', '0')) { - status = Loader::ResultStatus::ErrorNCA0; - return; - } - - NCAHeader dec_header{}; - Core::Crypto::AESCipher<Core::Crypto::Key256> cipher( - keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS); - cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200, - Core::Crypto::Op::Decrypt); - if (IsValidNCA(dec_header)) { - header = dec_header; - encrypted = true; - } else { - if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) { - status = Loader::ResultStatus::ErrorNCA2; - return; - } - if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) { - status = Loader::ResultStatus::ErrorNCA0; - return; - } - - if (!keys.HasKey(Core::Crypto::S256KeyType::Header)) - status = Loader::ResultStatus::ErrorMissingHeaderKey; - else - status = Loader::ResultStatus::ErrorIncorrectHeaderKey; - return; - } - } - - has_rights_id = std::find_if_not(header.rights_id.begin(), header.rights_id.end(), - [](char c) { return c == '\0'; }) != header.rights_id.end(); - - const std::ptrdiff_t number_sections = - std::count_if(std::begin(header.section_tables), std::end(header.section_tables), - [](NCASectionTableEntry entry) { return entry.media_offset > 0; }); - - std::vector<NCASectionHeader> sections(number_sections); - const auto length_sections = SECTION_HEADER_SIZE * number_sections; - - if (encrypted) { - auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET); - Core::Crypto::AESCipher<Core::Crypto::Key256> cipher( - keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS); - cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE, - Core::Crypto::Op::Decrypt); - } else { - file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET); - } - - is_update = std::find_if(sections.begin(), sections.end(), [](const NCASectionHeader& header) { - return header.raw.header.crypto_type == NCASectionCryptoType::BKTR; - }) != sections.end(); - ivfc_offset = 0; - - for (std::ptrdiff_t i = 0; i < number_sections; ++i) { - auto section = sections[i]; - - if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) { - const std::size_t base_offset = - header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER; - ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; - const std::size_t romfs_offset = base_offset + ivfc_offset; - const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size; - auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset); - auto dec = Decrypt(section, raw, romfs_offset); - - if (dec == nullptr) { - if (status != Loader::ResultStatus::Success) - return; - if (has_rights_id) - status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek; - else - status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey; - return; - } - - if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) { - if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') || - section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) { - status = Loader::ResultStatus::ErrorBadBKTRHeader; - return; - } - - if (section.bktr.relocation.offset + section.bktr.relocation.size != - section.bktr.subsection.offset) { - status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation; - return; - } - - const u64 size = - MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset - - header.section_tables[i].media_offset); - if (section.bktr.subsection.offset + section.bktr.subsection.size != size) { - status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd; - return; - } - - const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; - RelocationBlock relocation_block{}; - if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) != - sizeof(RelocationBlock)) { - status = Loader::ResultStatus::ErrorBadRelocationBlock; - return; - } - SubsectionBlock subsection_block{}; - if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) != - sizeof(RelocationBlock)) { - status = Loader::ResultStatus::ErrorBadSubsectionBlock; - return; - } - - std::vector<RelocationBucketRaw> relocation_buckets_raw( - (section.bktr.relocation.size - sizeof(RelocationBlock)) / - sizeof(RelocationBucketRaw)); - if (dec->ReadBytes(relocation_buckets_raw.data(), - section.bktr.relocation.size - sizeof(RelocationBlock), - section.bktr.relocation.offset + sizeof(RelocationBlock) - - offset) != - section.bktr.relocation.size - sizeof(RelocationBlock)) { - status = Loader::ResultStatus::ErrorBadRelocationBuckets; - return; - } - - std::vector<SubsectionBucketRaw> subsection_buckets_raw( - (section.bktr.subsection.size - sizeof(SubsectionBlock)) / - sizeof(SubsectionBucketRaw)); - if (dec->ReadBytes(subsection_buckets_raw.data(), - section.bktr.subsection.size - sizeof(SubsectionBlock), - section.bktr.subsection.offset + sizeof(SubsectionBlock) - - offset) != - section.bktr.subsection.size - sizeof(SubsectionBlock)) { - status = Loader::ResultStatus::ErrorBadSubsectionBuckets; - return; - } - - std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size()); - std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(), - relocation_buckets.begin(), &ConvertRelocationBucketRaw); - std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size()); - std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(), - subsection_buckets.begin(), &ConvertSubsectionBucketRaw); - - u32 ctr_low; - std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low)); - subsection_buckets.back().entries.push_back( - {section.bktr.relocation.offset, {0}, ctr_low}); - subsection_buckets.back().entries.push_back({size, {0}, 0}); - - boost::optional<Core::Crypto::Key128> key = boost::none; - if (encrypted) { - if (has_rights_id) { - status = Loader::ResultStatus::Success; - key = GetTitlekey(); - if (key == boost::none) { - status = Loader::ResultStatus::ErrorMissingTitlekey; - return; - } - } else { - key = GetKeyAreaKey(NCASectionCryptoType::BKTR); - if (key == boost::none) { - status = Loader::ResultStatus::ErrorMissingKeyAreaKey; - return; - } - } - } - - if (bktr_base_romfs == nullptr) { - status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS; - return; - } - - auto bktr = std::make_shared<BKTR>( - bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset), - relocation_block, relocation_buckets, subsection_block, subsection_buckets, - encrypted, encrypted ? key.get() : Core::Crypto::Key128{}, base_offset, - bktr_base_ivfc_offset, section.raw.section_ctr); - - // BKTR applies to entire IVFC, so make an offset version to level 6 - - files.push_back(std::make_shared<OffsetVfsFile>( - bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset)); - romfs = files.back(); - } else { - files.push_back(std::move(dec)); - romfs = files.back(); - } - } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) { - u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) * - MEDIA_OFFSET_MULTIPLIER) + - section.pfs0.pfs0_header_offset; - u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset - - header.section_tables[i].media_offset); - auto dec = - Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset); - if (dec != nullptr) { - auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec)); - - if (npfs->GetStatus() == Loader::ResultStatus::Success) { - dirs.push_back(std::move(npfs)); - if (IsDirectoryExeFS(dirs.back())) - exefs = dirs.back(); - } else { - if (has_rights_id) - status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek; - else - status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey; - return; - } - } else { - if (status != Loader::ResultStatus::Success) - return; - if (has_rights_id) - status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek; - else - status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey; - return; - } - } - } - - status = Loader::ResultStatus::Success; -} - -NCA::~NCA() = default; - Loader::ResultStatus NCA::GetStatus() const { return status; } diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index f9f66cae9..1c903cd3f 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -73,8 +73,6 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) { return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr; } -bool IsValidNCA(const NCAHeader& header); - // An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner. // After construction, use GetStatus to determine if the file is valid and ready to be used. class NCA : public ReadOnlyVfsDirectory { @@ -106,10 +104,19 @@ protected: bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; private: + bool CheckSupportedNCA(const NCAHeader& header); + bool HandlePotentialHeaderDecryption(); + + std::vector<NCASectionHeader> ReadSectionHeaders() const; + bool ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_base_ivfc_offset); + bool ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry, + u64 bktr_base_ivfc_offset); + bool ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry); + u8 GetCryptoRevision() const; boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const; boost::optional<Core::Crypto::Key128> GetTitlekey(); - VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset); + VirtualFile Decrypt(const NCASectionHeader& header, VirtualFile in, u64 starting_offset); std::vector<VirtualDir> dirs; std::vector<VirtualFile> files; @@ -118,15 +125,15 @@ private: VirtualDir exefs = nullptr; VirtualFile file; VirtualFile bktr_base_romfs; - u64 ivfc_offset; + u64 ivfc_offset = 0; NCAHeader header{}; bool has_rights_id{}; Loader::ResultStatus status{}; - bool encrypted; - bool is_update; + bool encrypted = false; + bool is_update = false; Core::Crypto::KeyManager keys; }; diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 47f2ab9e0..ef1aaebbb 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -51,6 +51,13 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr meta.title_id); } + if (meta.type == SaveDataType::DeviceSaveData && meta.user_id != u128{0, 0}) { + LOG_WARNING(Service_FS, + "Possibly incorrect SaveDataDescriptor, type is DeviceSaveData but user_id is " + "non-zero ({:016X}{:016X})", + meta.user_id[1], meta.user_id[0]); + } + std::string save_directory = GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); @@ -92,6 +99,9 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ case SaveDataSpaceId::NandUser: out = "/user/"; break; + case SaveDataSpaceId::TemporaryStorage: + out = "/temp/"; + break; default: ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); } @@ -100,10 +110,11 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ case SaveDataType::SystemSaveData: return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]); case SaveDataType::SaveData: + case SaveDataType::DeviceSaveData: return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], title_id); case SaveDataType::TemporaryStorage: - return fmt::format("{}temp/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], + return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], title_id); default: ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index edad5f1b1..68d5376cb 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -77,7 +77,8 @@ HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_ses HLERequestContext::~HLERequestContext() = default; -void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { +void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, + bool incoming) { IPC::RequestParser rp(src_cmdbuf); command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>()); @@ -94,8 +95,6 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { rp.Skip(2, false); } if (incoming) { - auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); - // Populate the object lists with the data in the IPC request. for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) { copy_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>())); @@ -189,10 +188,9 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { rp.Skip(1, false); // The command is actually an u64, but we don't use the high part. } -ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, - Process& src_process, - HandleTable& src_table) { - ParseCommandBuffer(src_cmdbuf, true); +ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTable& handle_table, + u32_le* src_cmdbuf) { + ParseCommandBuffer(handle_table, src_cmdbuf, true); if (command_header->type == IPC::CommandType::Close) { // Close does not populate the rest of the IPC header return RESULT_SUCCESS; @@ -207,14 +205,17 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb return RESULT_SUCCESS; } -ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) { +ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { + auto& owner_process = *thread.GetOwnerProcess(); + auto& handle_table = owner_process.GetHandleTable(); + std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf; - Memory::ReadBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(), + Memory::ReadBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), dst_cmdbuf.size() * sizeof(u32)); // The header was already built in the internal command buffer. Attempt to parse it to verify // the integrity and then copy it over to the target command buffer. - ParseCommandBuffer(cmd_buf.data(), false); + ParseCommandBuffer(handle_table, cmd_buf.data(), false); // The data_size already includes the payload header, the padding and the domain header. std::size_t size = data_payload_offset + command_header->data_size - @@ -236,8 +237,6 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) ASSERT(copy_objects.size() == handle_descriptor_header->num_handles_to_copy); ASSERT(move_objects.size() == handle_descriptor_header->num_handles_to_move); - auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); - // We don't make a distinction between copy and move handles when translating since HLE // services don't deal with handles directly. However, the guest applications might check // for specific values in each of these descriptors. @@ -268,7 +267,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) } // Copy the translated command buffer back into the thread's command buffer area. - Memory::WriteBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(), + Memory::WriteBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), dst_cmdbuf.size() * sizeof(u32)); return RESULT_SUCCESS; diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 894479ee0..f01491daa 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -24,10 +24,10 @@ class ServiceFrameworkBase; namespace Kernel { class Domain; +class Event; class HandleTable; class HLERequestContext; class Process; -class Event; /** * Interface implemented by HLE Session handlers. @@ -126,13 +126,12 @@ public: u64 timeout, WakeupCallback&& callback, Kernel::SharedPtr<Kernel::Event> event = nullptr); - void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming); - /// Populates this context with data from the requesting process/thread. - ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process, - HandleTable& src_table); + ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table, + u32_le* src_cmdbuf); + /// Writes data from this context back to the requesting process/thread. - ResultCode WriteToOutgoingCommandBuffer(const Thread& thread); + ResultCode WriteToOutgoingCommandBuffer(Thread& thread); u32_le GetCommand() const { return command; @@ -255,6 +254,8 @@ public: std::string Description() const; private: + void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming); + std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; SharedPtr<Kernel::ServerSession> server_session; // TODO(yuriks): Check common usage of this and optimize size accordingly diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index bd680adfe..4b6b32dd5 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -118,7 +118,6 @@ struct KernelCore::Impl { process_list.clear(); current_process = nullptr; - handle_table.Clear(); resource_limits.fill(nullptr); thread_wakeup_callback_handle_table.Clear(); @@ -209,7 +208,6 @@ struct KernelCore::Impl { std::vector<SharedPtr<Process>> process_list; Process* current_process = nullptr; - Kernel::HandleTable handle_table; std::array<SharedPtr<ResourceLimit>, 4> resource_limits; /// The event type of the generic timer callback event @@ -241,14 +239,6 @@ void KernelCore::Shutdown() { impl->Shutdown(); } -Kernel::HandleTable& KernelCore::HandleTable() { - return impl->handle_table; -} - -const Kernel::HandleTable& KernelCore::HandleTable() const { - return impl->handle_table; -} - SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory( ResourceLimitCategory category) const { return impl->resource_limits.at(static_cast<std::size_t>(category)); diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 41554821f..7f822d524 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -47,12 +47,6 @@ public: /// Clears all resources in use by the kernel instance. void Shutdown(); - /// Provides a reference to the handle table. - Kernel::HandleTable& HandleTable(); - - /// Provides a const reference to the handle table. - const Kernel::HandleTable& HandleTable() const; - /// Retrieves a shared pointer to a ResourceLimit identified by the given category. SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const; diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index f2816943a..148478488 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -13,6 +13,7 @@ #include <boost/container/static_vector.hpp> #include "common/bit_field.h" #include "common/common_types.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/vm_manager.h" @@ -142,6 +143,16 @@ public: return vm_manager; } + /// Gets a reference to the process' handle table. + HandleTable& GetHandleTable() { + return handle_table; + } + + /// Gets a const reference to the process' handle table. + const HandleTable& GetHandleTable() const { + return handle_table; + } + /// Gets the current status of the process ProcessStatus GetStatus() const { return status; @@ -294,6 +305,9 @@ private: /// specified by metadata provided to the process during loading. bool is_64bit_process = true; + /// Per-process handle table for storing created object handles in. + HandleTable handle_table; + std::string name; }; diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 1ece691c7..5fc320403 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -107,8 +107,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) { // similar. Kernel::HLERequestContext context(this); u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress()); - context.PopulateFromIncomingCommandBuffer(cmd_buf, *Core::CurrentProcess(), - kernel.HandleTable()); + context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); ResultCode result = RESULT_SUCCESS; // If the session has been converted to a domain, handle the domain request diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 3e5f11f2b..67ea67666 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -8,6 +8,7 @@ #include <mutex> #include <vector> +#include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" #include "common/microprofile.h" @@ -36,9 +37,6 @@ namespace Kernel { namespace { -constexpr bool Is4KBAligned(VAddr address) { - return (address & 0xFFF) == 0; -} // Checks if address + size is greater than the given address // This can return false if the size causes an overflow of a 64-bit type @@ -69,11 +67,11 @@ bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) { // in the same order. ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_addr, VAddr src_addr, u64 size) { - if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) { + if (!Common::Is4KBAligned(dst_addr) || !Common::Is4KBAligned(src_addr)) { return ERR_INVALID_ADDRESS; } - if (size == 0 || !Is4KBAligned(size)) { + if (size == 0 || !Common::Is4KBAligned(size)) { return ERR_INVALID_SIZE; } @@ -191,14 +189,15 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address CASCADE_RESULT(client_session, client_port->Connect()); // Return the client session - CASCADE_RESULT(*out_handle, kernel.HandleTable().Create(client_session)); + auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + CASCADE_RESULT(*out_handle, handle_table.Create(client_session)); return RESULT_SUCCESS; } /// Makes a blocking IPC call to an OS service. static ResultCode SendSyncRequest(Handle handle) { - auto& kernel = Core::System::GetInstance().Kernel(); - SharedPtr<ClientSession> session = kernel.HandleTable().Get<ClientSession>(handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + SharedPtr<ClientSession> session = handle_table.Get<ClientSession>(handle); if (!session) { LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle); return ERR_INVALID_HANDLE; @@ -217,8 +216,8 @@ static ResultCode SendSyncRequest(Handle handle) { static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) { LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); - auto& kernel = Core::System::GetInstance().Kernel(); - const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); if (!thread) { return ERR_INVALID_HANDLE; } @@ -231,8 +230,8 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) { static ResultCode GetProcessId(u32* process_id, Handle process_handle) { LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle); - auto& kernel = Core::System::GetInstance().Kernel(); - const SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const SharedPtr<Process> process = handle_table.Get<Process>(process_handle); if (!process) { return ERR_INVALID_HANDLE; } @@ -275,11 +274,11 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 using ObjectPtr = Thread::ThreadWaitObjects::value_type; Thread::ThreadWaitObjects objects(handle_count); - auto& kernel = Core::System::GetInstance().Kernel(); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); for (u64 i = 0; i < handle_count; ++i) { const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle)); - const auto object = kernel.HandleTable().Get<WaitObject>(handle); + const auto object = handle_table.Get<WaitObject>(handle); if (object == nullptr) { return ERR_INVALID_HANDLE; @@ -327,8 +326,8 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 static ResultCode CancelSynchronization(Handle thread_handle) { LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); - auto& kernel = Core::System::GetInstance().Kernel(); - const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); if (!thread) { return ERR_INVALID_HANDLE; } @@ -352,7 +351,11 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, return ERR_INVALID_ADDRESS_STATE; } - auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); + if (!Common::IsWordAligned(mutex_addr)) { + return ERR_INVALID_ADDRESS; + } + + auto& handle_table = Core::CurrentProcess()->GetHandleTable(); return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle, requesting_thread_handle); } @@ -365,6 +368,10 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) { return ERR_INVALID_ADDRESS_STATE; } + if (!Common::IsWordAligned(mutex_addr)) { + return ERR_INVALID_ADDRESS; + } + return Mutex::Release(mutex_addr); } @@ -448,25 +455,12 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) case GetInfoType::RandomEntropy: *result = 0; break; - case GetInfoType::AddressSpaceBaseAddr: - *result = vm_manager.GetCodeRegionBaseAddress(); + case GetInfoType::ASLRRegionBaseAddr: + *result = vm_manager.GetASLRRegionBaseAddress(); break; - case GetInfoType::AddressSpaceSize: { - const u64 width = vm_manager.GetAddressSpaceWidth(); - - switch (width) { - case 32: - *result = 0xFFE00000; - break; - case 36: - *result = 0xFF8000000; - break; - case 39: - *result = 0x7FF8000000; - break; - } + case GetInfoType::ASLRRegionSize: + *result = vm_manager.GetASLRRegionSize(); break; - } case GetInfoType::NewMapRegionBaseAddr: *result = vm_manager.GetNewMapRegionBaseAddress(); break; @@ -506,13 +500,12 @@ static ResultCode SetThreadActivity(Handle handle, u32 unknown) { static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle); - auto& kernel = Core::System::GetInstance().Kernel(); - const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle); + const auto* current_process = Core::CurrentProcess(); + const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); if (!thread) { return ERR_INVALID_HANDLE; } - const auto* current_process = Core::CurrentProcess(); if (thread->GetOwnerProcess() != current_process) { return ERR_INVALID_HANDLE; } @@ -538,10 +531,11 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { /// Gets the priority for the specified thread static ResultCode GetThreadPriority(u32* priority, Handle handle) { - auto& kernel = Core::System::GetInstance().Kernel(); - const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle); - if (!thread) + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle); + if (!thread) { return ERR_INVALID_HANDLE; + } *priority = thread->GetPriority(); return RESULT_SUCCESS; @@ -553,14 +547,15 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { return ERR_INVALID_THREAD_PRIORITY; } - auto& kernel = Core::System::GetInstance().Kernel(); - SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle); - if (!thread) + const auto* const current_process = Core::CurrentProcess(); + SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); + if (!thread) { return ERR_INVALID_HANDLE; + } // Note: The kernel uses the current process's resource limit instead of // the one from the thread owner's resource limit. - const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); + const ResourceLimit& resource_limit = current_process->GetResourceLimit(); if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { return ERR_NOT_AUTHORIZED; } @@ -583,14 +578,18 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", shared_memory_handle, addr, size, permissions); - if (!Is4KBAligned(addr)) { + if (!Common::Is4KBAligned(addr)) { return ERR_INVALID_ADDRESS; } - if (size == 0 || !Is4KBAligned(size)) { + if (size == 0 || !Common::Is4KBAligned(size)) { return ERR_INVALID_SIZE; } + if (!IsValidAddressRange(addr, size)) { + return ERR_INVALID_ADDRESS_STATE; + } + const auto permissions_type = static_cast<MemoryPermission>(permissions); if (permissions_type != MemoryPermission::Read && permissions_type != MemoryPermission::ReadWrite) { @@ -598,46 +597,61 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s return ERR_INVALID_MEMORY_PERMISSIONS; } - auto& kernel = Core::System::GetInstance().Kernel(); - auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); + auto* const current_process = Core::CurrentProcess(); + auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); if (!shared_memory) { return ERR_INVALID_HANDLE; } - return shared_memory->Map(Core::CurrentProcess(), addr, permissions_type, - MemoryPermission::DontCare); + const auto& vm_manager = current_process->VMManager(); + if (!vm_manager.IsWithinASLRRegion(addr, size)) { + return ERR_INVALID_MEMORY_RANGE; + } + + return shared_memory->Map(current_process, addr, permissions_type, MemoryPermission::DontCare); } static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", shared_memory_handle, addr, size); - if (!Is4KBAligned(addr)) { + if (!Common::Is4KBAligned(addr)) { return ERR_INVALID_ADDRESS; } - if (size == 0 || !Is4KBAligned(size)) { + if (size == 0 || !Common::Is4KBAligned(size)) { return ERR_INVALID_SIZE; } - auto& kernel = Core::System::GetInstance().Kernel(); - auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); + if (!IsValidAddressRange(addr, size)) { + return ERR_INVALID_ADDRESS_STATE; + } + + auto* const current_process = Core::CurrentProcess(); + auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); + if (!shared_memory) { + return ERR_INVALID_HANDLE; + } + + const auto& vm_manager = current_process->VMManager(); + if (!vm_manager.IsWithinASLRRegion(addr, size)) { + return ERR_INVALID_MEMORY_RANGE; + } - return shared_memory->Unmap(Core::CurrentProcess(), addr); + return shared_memory->Unmap(current_process, addr); } /// Query process memory static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/, Handle process_handle, u64 addr) { - - auto& kernel = Core::System::GetInstance().Kernel(); - SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + SharedPtr<Process> process = handle_table.Get<Process>(process_handle); if (!process) { return ERR_INVALID_HANDLE; } auto vma = process->VMManager().FindVMA(addr); memory_info->attributes = 0; - if (vma == Core::CurrentProcess()->VMManager().vma_map.end()) { + if (vma == process->VMManager().vma_map.end()) { memory_info->base_address = 0; memory_info->permission = static_cast<u32>(VMAPermission::None); memory_info->size = 0; @@ -678,20 +692,19 @@ static void ExitProcess() { /// Creates a new thread static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, u32 priority, s32 processor_id) { - std::string name = fmt::format("thread-{:X}", entry_point); - if (priority > THREADPRIO_LOWEST) { return ERR_INVALID_THREAD_PRIORITY; } - const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); + auto* const current_process = Core::CurrentProcess(); + const ResourceLimit& resource_limit = current_process->GetResourceLimit(); if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { return ERR_NOT_AUTHORIZED; } if (processor_id == THREADPROCESSORID_DEFAULT) { // Set the target CPU to the one specified in the process' exheader. - processor_id = Core::CurrentProcess()->GetDefaultProcessorID(); + processor_id = current_process->GetDefaultProcessorID(); ASSERT(processor_id != THREADPROCESSORID_DEFAULT); } @@ -706,11 +719,13 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V return ERR_INVALID_PROCESSOR_ID; } + const std::string name = fmt::format("thread-{:X}", entry_point); auto& kernel = Core::System::GetInstance().Kernel(); CASCADE_RESULT(SharedPtr<Thread> thread, Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, - *Core::CurrentProcess())); - const auto new_guest_handle = kernel.HandleTable().Create(thread); + *current_process)); + + const auto new_guest_handle = current_process->GetHandleTable().Create(thread); if (new_guest_handle.Failed()) { return new_guest_handle.Code(); } @@ -731,8 +746,8 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V static ResultCode StartThread(Handle thread_handle) { LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); - auto& kernel = Core::System::GetInstance().Kernel(); - const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); if (!thread) { return ERR_INVALID_HANDLE; } @@ -779,8 +794,8 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", mutex_addr, condition_variable_addr, thread_handle, nano_seconds); - auto& kernel = Core::System::GetInstance().Kernel(); - SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); ASSERT(thread); CASCADE_CODE(Mutex::Release(mutex_addr)); @@ -891,9 +906,9 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target mutex_val | Mutex::MutexHasWaitersFlag)); // The mutex is already owned by some other thread, make this thread wait on it. - auto& kernel = Core::System::GetInstance().Kernel(); - Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); - auto owner = kernel.HandleTable().Get<Thread>(owner_handle); + const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + auto owner = handle_table.Get<Thread>(owner_handle); ASSERT(owner); ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); thread->InvalidateWakeupCallback(); @@ -972,16 +987,16 @@ static u64 GetSystemTick() { static ResultCode CloseHandle(Handle handle) { LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); - auto& kernel = Core::System::GetInstance().Kernel(); - return kernel.HandleTable().Close(handle); + auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + return handle_table.Close(handle); } /// Reset an event static ResultCode ResetSignal(Handle handle) { LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); - auto& kernel = Core::System::GetInstance().Kernel(); - auto event = kernel.HandleTable().Get<Event>(handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + auto event = handle_table.Get<Event>(handle); ASSERT(event != nullptr); @@ -1000,8 +1015,8 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) { LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); - auto& kernel = Core::System::GetInstance().Kernel(); - const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); if (!thread) { return ERR_INVALID_HANDLE; } @@ -1016,8 +1031,8 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle, mask, core); - auto& kernel = Core::System::GetInstance().Kernel(); - const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); if (!thread) { return ERR_INVALID_HANDLE; } @@ -1078,7 +1093,7 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss } auto& kernel = Core::System::GetInstance().Kernel(); - auto& handle_table = kernel.HandleTable(); + auto& handle_table = Core::CurrentProcess()->GetHandleTable(); auto shared_mem_handle = SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size, local_perms, remote_perms); @@ -1090,10 +1105,12 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss static ResultCode ClearEvent(Handle handle) { LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); - auto& kernel = Core::System::GetInstance().Kernel(); - SharedPtr<Event> evt = kernel.HandleTable().Get<Event>(handle); - if (evt == nullptr) + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + SharedPtr<Event> evt = handle_table.Get<Event>(handle); + if (evt == nullptr) { return ERR_INVALID_HANDLE; + } + evt->Clear(); return RESULT_SUCCESS; } @@ -1106,8 +1123,8 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) { Status, }; - const auto& kernel = Core::System::GetInstance().Kernel(); - const auto process = kernel.HandleTable().Get<Process>(process_handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const auto process = handle_table.Get<Process>(process_handle); if (!process) { return ERR_INVALID_HANDLE; } diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h index 70148c4fe..554a5e328 100644 --- a/src/core/hle/kernel/svc.h +++ b/src/core/hle/kernel/svc.h @@ -41,8 +41,8 @@ enum class GetInfoType : u64 { RandomEntropy = 11, PerformanceCounter = 0xF0000002, // 2.0.0+ - AddressSpaceBaseAddr = 12, - AddressSpaceSize = 13, + ASLRRegionBaseAddr = 12, + ASLRRegionSize = 13, NewMapRegionBaseAddr = 14, NewMapRegionSize = 15, // 3.0.0+ diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 35ec98c1a..59bc9e0af 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -266,7 +266,7 @@ SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 pri SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); // Register 1 must be a handle to the main thread - const Handle guest_handle = kernel.HandleTable().Create(thread).Unwrap(); + const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap(); thread->SetGuestHandle(guest_handle); thread->GetContext().cpu_registers[1] = guest_handle; diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index e412309fd..e1a34eef1 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -393,30 +393,35 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty switch (type) { case FileSys::ProgramAddressSpaceType::Is32Bit: + case FileSys::ProgramAddressSpaceType::Is32BitNoMap: address_space_width = 32; code_region_base = 0x200000; code_region_end = code_region_base + 0x3FE00000; - map_region_size = 0x40000000; - heap_region_size = 0x40000000; + aslr_region_base = 0x200000; + aslr_region_end = aslr_region_base + 0xFFE00000; + if (type == FileSys::ProgramAddressSpaceType::Is32Bit) { + map_region_size = 0x40000000; + heap_region_size = 0x40000000; + } else { + map_region_size = 0; + heap_region_size = 0x80000000; + } break; case FileSys::ProgramAddressSpaceType::Is36Bit: address_space_width = 36; code_region_base = 0x8000000; code_region_end = code_region_base + 0x78000000; + aslr_region_base = 0x8000000; + aslr_region_end = aslr_region_base + 0xFF8000000; map_region_size = 0x180000000; heap_region_size = 0x180000000; break; - case FileSys::ProgramAddressSpaceType::Is32BitNoMap: - address_space_width = 32; - code_region_base = 0x200000; - code_region_end = code_region_base + 0x3FE00000; - map_region_size = 0; - heap_region_size = 0x80000000; - break; case FileSys::ProgramAddressSpaceType::Is39Bit: address_space_width = 39; code_region_base = 0x8000000; code_region_end = code_region_base + 0x80000000; + aslr_region_base = 0x8000000; + aslr_region_end = aslr_region_base + 0x7FF8000000; map_region_size = 0x1000000000; heap_region_size = 0x180000000; new_map_region_size = 0x80000000; @@ -490,6 +495,38 @@ u64 VMManager::GetAddressSpaceWidth() const { return address_space_width; } +VAddr VMManager::GetASLRRegionBaseAddress() const { + return aslr_region_base; +} + +VAddr VMManager::GetASLRRegionEndAddress() const { + return aslr_region_end; +} + +u64 VMManager::GetASLRRegionSize() const { + return aslr_region_end - aslr_region_base; +} + +bool VMManager::IsWithinASLRRegion(VAddr begin, u64 size) const { + const VAddr range_end = begin + size; + const VAddr aslr_start = GetASLRRegionBaseAddress(); + const VAddr aslr_end = GetASLRRegionEndAddress(); + + if (aslr_start > begin || begin > range_end || range_end - 1 > aslr_end - 1) { + return false; + } + + if (range_end > heap_region_base && heap_region_end > begin) { + return false; + } + + if (range_end > map_region_base && map_region_end > begin) { + return false; + } + + return true; +} + VAddr VMManager::GetCodeRegionBaseAddress() const { return code_region_base; } diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 015559a64..84c890224 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -205,6 +205,18 @@ public: /// Gets the address space width in bits. u64 GetAddressSpaceWidth() const; + /// Gets the base address of the ASLR region. + VAddr GetASLRRegionBaseAddress() const; + + /// Gets the end address of the ASLR region. + VAddr GetASLRRegionEndAddress() const; + + /// Determines whether or not the specified address range is within the ASLR region. + bool IsWithinASLRRegion(VAddr address, u64 size) const; + + /// Gets the size of the ASLR region + u64 GetASLRRegionSize() const; + /// Gets the base address of the code region. VAddr GetCodeRegionBaseAddress() const; @@ -306,6 +318,9 @@ private: VAddr address_space_base = 0; VAddr address_space_end = 0; + VAddr aslr_region_base = 0; + VAddr aslr_region_end = 0; + VAddr code_region_base = 0; VAddr code_region_end = 0; diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 69bfce1c1..ecf72ae24 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -15,6 +15,7 @@ #include "core/hle/service/am/idle.h" #include "core/hle/service/am/omm.h" #include "core/hle/service/am/spsm.h" +#include "core/hle/service/am/tcap.h" #include "core/hle/service/apm/apm.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/nvflinger/nvflinger.h" @@ -26,13 +27,18 @@ namespace Service::AM { IWindowController::IWindowController() : ServiceFramework("IWindowController") { + // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "CreateWindow"}, {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, {11, nullptr, "ReleaseForegroundRights"}, {12, nullptr, "RejectToChangeIntoBackground"}, + {20, nullptr, "SetAppletWindowVisibility"}, + {21, nullptr, "SetAppletGpuTimeSlice"}, }; + // clang-format on + RegisterHandlers(functions); } @@ -87,6 +93,7 @@ void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestCo } IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") { + // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetLastForegroundCaptureImage"}, {1, nullptr, "UpdateLastForegroundCaptureImage"}, @@ -117,7 +124,11 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController" {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"}, {26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"}, {27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"}, + // 6.0.0+ + {28, nullptr, "TakeScreenShotOfOwnLayerEx"}, }; + // clang-format on + RegisterHandlers(functions); } @@ -128,6 +139,7 @@ IDebugFunctions::~IDebugFunctions() = default; ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) { + // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "Exit"}, {1, &ISelfController::LockExit, "LockExit"}, @@ -136,10 +148,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger {4, nullptr, "LeaveFatalSection"}, {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"}, {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"}, - {11, &ISelfController::SetOperationModeChangedNotification, - "SetOperationModeChangedNotification"}, - {12, &ISelfController::SetPerformanceModeChangedNotification, - "SetPerformanceModeChangedNotification"}, + {11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"}, + {12, &ISelfController::SetPerformanceModeChangedNotification, "SetPerformanceModeChangedNotification"}, {13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"}, {14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"}, {15, nullptr, "SetScreenShotAppletIdentityInfo"}, @@ -165,7 +175,12 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger {69, nullptr, "IsAutoSleepDisabled"}, {70, nullptr, "ReportMultimediaError"}, {80, nullptr, "SetWirelessPriorityMode"}, + {90, nullptr, "GetAccumulatedSuspendedTickValue"}, + {91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"}, + {1000, nullptr, "GetDebugStorageChannel"}, }; + // clang-format on + RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); @@ -312,6 +327,7 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c } ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") { + // clang-format off static const FunctionInfo functions[] = { {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, {1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"}, @@ -336,11 +352,12 @@ ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter" {52, nullptr, "SwitchLcdBacklight"}, {55, nullptr, "IsInControllerFirmwareUpdateSection"}, {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"}, - {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, - "GetDefaultDisplayResolutionChangeEvent"}, + {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"}, {62, nullptr, "GetHdcpAuthenticationState"}, {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"}, }; + // clang-format on + RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); @@ -432,11 +449,14 @@ class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { public: explicit IStorageAccessor(std::vector<u8> buffer) : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) { + // clang-format off static const FunctionInfo functions[] = { {0, &IStorageAccessor::GetSize, "GetSize"}, {10, &IStorageAccessor::Write, "Write"}, {11, &IStorageAccessor::Read, "Read"}, }; + // clang-format on + RegisterHandlers(functions); } @@ -489,10 +509,13 @@ class IStorage final : public ServiceFramework<IStorage> { public: explicit IStorage(std::vector<u8> buffer) : ServiceFramework("IStorage"), buffer(std::move(buffer)) { + // clang-format off static const FunctionInfo functions[] = { {0, &IStorage::Open, "Open"}, {1, nullptr, "OpenTransferStorage"}, }; + // clang-format on + RegisterHandlers(functions); } @@ -512,6 +535,7 @@ private: class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { public: explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") { + // clang-format off static const FunctionInfo functions[] = { {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, {1, nullptr, "IsCompleted"}, @@ -532,6 +556,8 @@ public: {150, nullptr, "RequestForAppletToGetForeground"}, {160, nullptr, "GetIndirectLayerConsumerHandle"}, }; + // clang-format on + RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); @@ -624,13 +650,13 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) { } IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { + // clang-format off static const FunctionInfo functions[] = { {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"}, {10, nullptr, "CreateApplicationAndPushAndRequestToStart"}, {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"}, {12, nullptr, "CreateApplicationAndRequestToStart"}, - {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, - "CreateApplicationAndRequestToStartForQuest"}, + {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"}, {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"}, {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"}, {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, @@ -638,10 +664,10 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF {24, nullptr, "GetLaunchStorageInfoForDebug"}, {25, nullptr, "ExtendSaveData"}, {26, nullptr, "GetSaveDataSize"}, - {30, nullptr, "BeginBlockingHomeButtonShortAndLongPressed"}, - {31, nullptr, "EndBlockingHomeButtonShortAndLongPressed"}, - {32, nullptr, "BeginBlockingHomeButton"}, - {33, nullptr, "EndBlockingHomeButton"}, + {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"}, + {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"}, + {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"}, + {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"}, {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"}, {60, nullptr, "SetMediaPlaybackStateForApplication"}, @@ -664,11 +690,39 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF {1000, nullptr, "CreateMovieMaker"}, {1001, nullptr, "PrepareForJit"}, }; + // clang-format on + RegisterHandlers(functions); } IApplicationFunctions::~IApplicationFunctions() = default; +void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed( + Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed( + Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void IApplicationFunctions::BeginBlockingHomeButton(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { constexpr std::array<u8, 0x88> data{{ 0xca, 0x97, 0x94, 0xc7, // Magic @@ -776,9 +830,11 @@ void InstallInterfaces(SM::ServiceManager& service_manager, std::make_shared<IdleSys>()->InstallAsService(service_manager); std::make_shared<OMM>()->InstallAsService(service_manager); std::make_shared<SPSM>()->InstallAsService(service_manager); + std::make_shared<TCAP>()->InstallAsService(service_manager); } IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions") { + // clang-format off static const FunctionInfo functions[] = { {10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"}, {11, nullptr, "LockForeground"}, @@ -787,7 +843,10 @@ IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions" {21, nullptr, "GetPopFromGeneralChannelEvent"}, {30, nullptr, "GetHomeButtonWriterLockAccessor"}, {31, nullptr, "GetWriterLockAccessorEx"}, + {100, nullptr, "PopRequestLaunchApplicationForDebug"}, }; + // clang-format on + RegisterHandlers(functions); } @@ -800,6 +859,7 @@ void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx) } IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStateController") { + // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "RequestToEnterSleep"}, {1, nullptr, "EnterSleep"}, @@ -813,18 +873,23 @@ IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStat {14, nullptr, "ShouldSleepOnBoot"}, {15, nullptr, "GetHdcpAuthenticationFailedEvent"}, }; + // clang-format on + RegisterHandlers(functions); } IGlobalStateController::~IGlobalStateController() = default; IApplicationCreator::IApplicationCreator() : ServiceFramework("IApplicationCreator") { + // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "CreateApplication"}, {1, nullptr, "PopLaunchRequestedApplication"}, {10, nullptr, "CreateSystemApplication"}, {100, nullptr, "PopFloatingApplicationForDevelopment"}, }; + // clang-format on + RegisterHandlers(functions); } @@ -832,6 +897,7 @@ IApplicationCreator::~IApplicationCreator() = default; IProcessWindingController::IProcessWindingController() : ServiceFramework("IProcessWindingController") { + // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetLaunchReason"}, {11, nullptr, "OpenCallingLibraryApplet"}, @@ -842,6 +908,8 @@ IProcessWindingController::IProcessWindingController() {40, nullptr, "ReserveToStartAndWaitAndUnwindThis"}, {41, nullptr, "ReserveToStartAndWait"}, }; + // clang-format on + RegisterHandlers(functions); } diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index b39b0d838..095f94851 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -154,6 +154,10 @@ private: void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx); void NotifyRunning(Kernel::HLERequestContext& ctx); void GetPseudoDeviceId(Kernel::HLERequestContext& ctx); + void BeginBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx); + void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx); + void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx); + void EndBlockingHomeButton(Kernel::HLERequestContext& ctx); }; class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp index 4296c255e..68ea778e8 100644 --- a/src/core/hle/service/am/applet_ae.cpp +++ b/src/core/hle/service/am/applet_ae.cpp @@ -211,6 +211,7 @@ void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) { AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) { + // clang-format off static const FunctionInfo functions[] = { {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"}, {200, &AppletAE::OpenLibraryAppletProxyOld, "OpenLibraryAppletProxyOld"}, @@ -218,7 +219,10 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {300, nullptr, "OpenOverlayAppletProxy"}, {350, nullptr, "OpenSystemApplicationProxy"}, {400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"}, + {401, nullptr, "GetSystemAppletControllerForDebug"}, }; + // clang-format on + RegisterHandlers(functions); } diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp index e45cf6e20..60717afd9 100644 --- a/src/core/hle/service/am/applet_oe.cpp +++ b/src/core/hle/service/am/applet_oe.cpp @@ -14,6 +14,7 @@ class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { public: explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) { + // clang-format off static const FunctionInfo functions[] = { {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"}, {1, &IApplicationProxy::GetSelfController, "GetSelfController"}, @@ -25,6 +26,8 @@ public: {20, &IApplicationProxy::GetApplicationFunctions, "GetApplicationFunctions"}, {1000, &IApplicationProxy::GetDebugFunctions, "GetDebugFunctions"}, }; + // clang-format on + RegisterHandlers(functions); } diff --git a/src/core/hle/service/am/idle.cpp b/src/core/hle/service/am/idle.cpp index 0e3088bc8..f814fe2c0 100644 --- a/src/core/hle/service/am/idle.cpp +++ b/src/core/hle/service/am/idle.cpp @@ -12,9 +12,9 @@ IdleSys::IdleSys() : ServiceFramework{"idle:sys"} { {0, nullptr, "GetAutoPowerDownEvent"}, {1, nullptr, "Unknown1"}, {2, nullptr, "Unknown2"}, - {3, nullptr, "Unknown3"}, - {4, nullptr, "Unknown4"}, - {5, nullptr, "Unknown5"}, + {3, nullptr, "SetHandlingContext"}, + {4, nullptr, "LoadAndApplySettings"}, + {5, nullptr, "ReportUserIsActive"}, }; // clang-format on diff --git a/src/core/hle/service/am/omm.cpp b/src/core/hle/service/am/omm.cpp index 1c37f849f..6ab3fb906 100644 --- a/src/core/hle/service/am/omm.cpp +++ b/src/core/hle/service/am/omm.cpp @@ -17,22 +17,24 @@ OMM::OMM() : ServiceFramework{"omm"} { {5, nullptr, "GetCradleStatus"}, {6, nullptr, "FadeInDisplay"}, {7, nullptr, "FadeOutDisplay"}, - {8, nullptr, "Unknown1"}, - {9, nullptr, "Unknown2"}, - {10, nullptr, "Unknown3"}, - {11, nullptr, "Unknown4"}, - {12, nullptr, "Unknown5"}, - {13, nullptr, "Unknown6"}, - {14, nullptr, "Unknown7"}, - {15, nullptr, "Unknown8"}, - {16, nullptr, "Unknown9"}, - {17, nullptr, "Unknown10"}, - {18, nullptr, "Unknown11"}, - {19, nullptr, "Unknown12"}, - {20, nullptr, "Unknown13"}, - {21, nullptr, "Unknown14"}, - {22, nullptr, "Unknown15"}, - {23, nullptr, "Unknown16"}, + {8, nullptr, "GetCradleFwVersion"}, + {9, nullptr, "NotifyCecSettingsChanged"}, + {10, nullptr, "SetOperationModePolicy"}, + {11, nullptr, "GetDefaultDisplayResolution"}, + {12, nullptr, "GetDefaultDisplayResolutionChangeEvent"}, + {13, nullptr, "UpdateDefaultDisplayResolution"}, + {14, nullptr, "ShouldSleepOnBoot"}, + {15, nullptr, "NotifyHdcpApplicationExecutionStarted"}, + {16, nullptr, "NotifyHdcpApplicationExecutionFinished"}, + {17, nullptr, "NotifyHdcpApplicationDrawingStarted"}, + {18, nullptr, "NotifyHdcpApplicationDrawingFinished"}, + {19, nullptr, "GetHdcpAuthenticationFailedEvent"}, + {20, nullptr, "GetHdcpAuthenticationFailedEmulationEnabled"}, + {21, nullptr, "SetHdcpAuthenticationFailedEmulation"}, + {22, nullptr, "GetHdcpStateChangeEvent"}, + {23, nullptr, "GetHdcpState"}, + {24, nullptr, "ShowCardUpdateProcessing"}, + {25, nullptr, "SetApplicationCecSettingsAndNotifyChanged"}, }; // clang-format on diff --git a/src/core/hle/service/am/tcap.cpp b/src/core/hle/service/am/tcap.cpp new file mode 100644 index 000000000..a75cbdda8 --- /dev/null +++ b/src/core/hle/service/am/tcap.cpp @@ -0,0 +1,23 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/am/tcap.h" + +namespace Service::AM { + +TCAP::TCAP() : ServiceFramework{"tcap"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetContinuousHighSkinTemperatureEvent"}, + {1, nullptr, "SetOperationMode"}, + {2, nullptr, "LoadAndApplySettings"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +TCAP::~TCAP() = default; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/tcap.h b/src/core/hle/service/am/tcap.h new file mode 100644 index 000000000..2021b55d1 --- /dev/null +++ b/src/core/hle/service/am/tcap.h @@ -0,0 +1,17 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +class TCAP final : public ServiceFramework<TCAP> { +public: + explicit TCAP(); + ~TCAP() override; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 518161bf7..428069df2 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -13,6 +13,7 @@ #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/event.h" #include "core/hle/kernel/process.h" #include "core/hle/service/aoc/aoc_u.h" #include "core/hle/service/filesystem/filesystem.h" @@ -55,9 +56,13 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs {5, &AOC_U::GetAddOnContentBaseId, "GetAddOnContentBaseId"}, {6, nullptr, "PrepareAddOnContentByApplicationId"}, {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"}, - {8, nullptr, "GetAddOnContentListChangedEvent"}, + {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"}, }; RegisterHandlers(functions); + + auto& kernel = Core::System::GetInstance().Kernel(); + aoc_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, + "GetAddOnContentListChanged:Event"); } AOC_U::~AOC_U() = default; @@ -130,6 +135,14 @@ void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } +void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AOC, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(aoc_change_event); +} + void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<AOC_U>()->InstallAsService(service_manager); } diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h index b3c7cab7a..68d94fdaa 100644 --- a/src/core/hle/service/aoc/aoc_u.h +++ b/src/core/hle/service/aoc/aoc_u.h @@ -18,8 +18,10 @@ private: void ListAddOnContent(Kernel::HLERequestContext& ctx); void GetAddOnContentBaseId(Kernel::HLERequestContext& ctx); void PrepareAddOnContent(Kernel::HLERequestContext& ctx); + void GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx); std::vector<u64> add_on_content; + Kernel::SharedPtr<Kernel::Event> aoc_change_event; }; /// Registers all AOC services with the specified service manager. diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 6073f4ecd..fac6785a5 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -22,20 +22,22 @@ class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { public: explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params) : ServiceFramework("IAudioRenderer") { + // clang-format off static const FunctionInfo functions[] = { - {0, &IAudioRenderer::GetAudioRendererSampleRate, "GetAudioRendererSampleRate"}, - {1, &IAudioRenderer::GetAudioRendererSampleCount, "GetAudioRendererSampleCount"}, - {2, &IAudioRenderer::GetAudioRendererMixBufferCount, "GetAudioRendererMixBufferCount"}, - {3, &IAudioRenderer::GetAudioRendererState, "GetAudioRendererState"}, - {4, &IAudioRenderer::RequestUpdateAudioRenderer, "RequestUpdateAudioRenderer"}, - {5, &IAudioRenderer::StartAudioRenderer, "StartAudioRenderer"}, - {6, &IAudioRenderer::StopAudioRenderer, "StopAudioRenderer"}, + {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, + {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"}, + {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"}, + {3, &IAudioRenderer::GetState, "GetState"}, + {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"}, + {5, &IAudioRenderer::Start, "Start"}, + {6, &IAudioRenderer::Stop, "Stop"}, {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, - {8, nullptr, "SetAudioRendererRenderingTimeLimit"}, - {9, nullptr, "GetAudioRendererRenderingTimeLimit"}, - {10, nullptr, "RequestUpdateAudioRendererAuto"}, + {8, nullptr, "SetRenderingTimeLimit"}, + {9, nullptr, "GetRenderingTimeLimit"}, + {10, nullptr, "RequestUpdateAuto"}, {11, nullptr, "ExecuteAudioRendererRendering"}, }; + // clang-format on RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); @@ -49,42 +51,42 @@ private: system_event->Signal(); } - void GetAudioRendererSampleRate(Kernel::HLERequestContext& ctx) { + void GetSampleRate(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(renderer->GetSampleRate()); LOG_DEBUG(Service_Audio, "called"); } - void GetAudioRendererSampleCount(Kernel::HLERequestContext& ctx) { + void GetSampleCount(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(renderer->GetSampleCount()); LOG_DEBUG(Service_Audio, "called"); } - void GetAudioRendererState(Kernel::HLERequestContext& ctx) { + void GetState(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(static_cast<u32>(renderer->GetStreamState())); LOG_DEBUG(Service_Audio, "called"); } - void GetAudioRendererMixBufferCount(Kernel::HLERequestContext& ctx) { + void GetMixBufferCount(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(renderer->GetMixBufferCount()); LOG_DEBUG(Service_Audio, "called"); } - void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) { + void RequestUpdate(Kernel::HLERequestContext& ctx) { ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer())); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_Audio, "(STUBBED) called"); } - void StartAudioRenderer(Kernel::HLERequestContext& ctx) { + void Start(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -92,7 +94,7 @@ private: LOG_WARNING(Service_Audio, "(STUBBED) called"); } - void StopAudioRenderer(Kernel::HLERequestContext& ctx) { + void Stop(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -129,6 +131,7 @@ public: {10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"}, {11, nullptr, "QueryAudioDeviceInputEvent"}, {12, nullptr, "QueryAudioDeviceOutputEvent"}, + {13, nullptr, "GetAudioSystemMasterVolumeSetting"}, }; RegisterHandlers(functions); diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index d40f18565..6701cb913 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp @@ -9,6 +9,7 @@ namespace Service::ES { class ETicket final : public ServiceFramework<ETicket> { public: explicit ETicket() : ServiceFramework{"es"} { + // clang-format off static const FunctionInfo functions[] = { {1, nullptr, "ImportTicket"}, {2, nullptr, "ImportTicketCertificateSet"}, @@ -37,15 +38,18 @@ public: {25, nullptr, "DeletePrepurchaseRecord"}, {26, nullptr, "DeleteAllPrepurchaseRecord"}, {27, nullptr, "CountPrepurchaseRecord"}, - {28, nullptr, "ListPrepurchaseRecord"}, + {28, nullptr, "ListPrepurchaseRecordRightsIds"}, {29, nullptr, "ListPrepurchaseRecordInfo"}, - {30, nullptr, "Unknown1"}, - {31, nullptr, "Unknown2"}, - {32, nullptr, "Unknown3"}, - {33, nullptr, "Unknown4"}, - {34, nullptr, "Unknown5"}, - {35, nullptr, "Unknown6"}, + {30, nullptr, "CountTicket"}, + {31, nullptr, "ListTicketRightsIds"}, + {32, nullptr, "CountPrepurchaseRecordEx"}, + {33, nullptr, "ListPrepurchaseRecordRightsIdsEx"}, + {34, nullptr, "GetEncryptedTicketSize"}, + {35, nullptr, "GetEncryptedTicketData"}, + {36, nullptr, "DeleteAllInactiveELicenseRequiredPersonalizedTicket"}, + {503, nullptr, "GetTitleKey"}, }; + // clang-format on RegisterHandlers(functions); } }; diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp new file mode 100644 index 000000000..0993a7815 --- /dev/null +++ b/src/core/hle/service/hid/controllers/controller_base.cpp @@ -0,0 +1,30 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/hid/controllers/controller_base.h" + +namespace Service::HID { + +ControllerBase::ControllerBase() = default; +ControllerBase::~ControllerBase() = default; + +void ControllerBase::ActivateController() { + if (is_activated) { + OnRelease(); + } + is_activated = true; + OnInit(); +} + +void ControllerBase::DeactivateController() { + if (is_activated) { + OnRelease(); + } + is_activated = false; +} + +bool ControllerBase::IsControllerActivated() const { + return is_activated; +} +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h new file mode 100644 index 000000000..f0e092b1b --- /dev/null +++ b/src/core/hle/service/hid/controllers/controller_base.h @@ -0,0 +1,45 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" +#include "common/swap.h" + +namespace Service::HID { +class ControllerBase { +public: + ControllerBase(); + virtual ~ControllerBase(); + + // Called when the controller is initialized + virtual void OnInit() = 0; + + // When the controller is released + virtual void OnRelease() = 0; + + // When the controller is requesting an update for the shared memory + virtual void OnUpdate(u8* data, std::size_t size) = 0; + + // Called when input devices should be loaded + virtual void OnLoadInputDevices() = 0; + + void ActivateController(); + + void DeactivateController(); + + bool IsControllerActivated() const; + +protected: + bool is_activated{false}; + + struct CommonHeader { + s64_le timestamp; + s64_le total_entry_count; + s64_le last_entry_index; + s64_le entry_count; + }; + static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size"); +}; +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp new file mode 100644 index 000000000..3d100763f --- /dev/null +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp @@ -0,0 +1,42 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> +#include "common/common_types.h" +#include "core/core_timing.h" +#include "core/hle/service/hid/controllers/debug_pad.h" + +namespace Service::HID { + +Controller_DebugPad::Controller_DebugPad() = default; +Controller_DebugPad::~Controller_DebugPad() = default; + +void Controller_DebugPad::OnInit() {} + +void Controller_DebugPad::OnRelease() {} + +void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) { + shared_memory.header.timestamp = CoreTiming::GetTicks(); + shared_memory.header.total_entry_count = 17; + + if (!IsControllerActivated()) { + shared_memory.header.entry_count = 0; + shared_memory.header.last_entry_index = 0; + return; + } + shared_memory.header.entry_count = 16; + + const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index]; + shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17; + auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index]; + + cur_entry.sampling_number = last_entry.sampling_number + 1; + cur_entry.sampling_number2 = cur_entry.sampling_number; + // TODO(ogniK): Update debug pad states + + std::memcpy(data, &shared_memory, sizeof(SharedMemory)); +} + +void Controller_DebugPad::OnLoadInputDevices() {} +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h new file mode 100644 index 000000000..62b4f2682 --- /dev/null +++ b/src/core/hle/service/hid/controllers/debug_pad.h @@ -0,0 +1,56 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/swap.h" +#include "core/hle/service/hid/controllers/controller_base.h" + +namespace Service::HID { +class Controller_DebugPad final : public ControllerBase { +public: + Controller_DebugPad(); + ~Controller_DebugPad() override; + + // Called when the controller is initialized + void OnInit() override; + + // When the controller is released + void OnRelease() override; + + // When the controller is requesting an update for the shared memory + void OnUpdate(u8* data, std::size_t size) override; + + // Called when input devices should be loaded + void OnLoadInputDevices() override; + +private: + struct AnalogStick { + s32_le x; + s32_le y; + }; + static_assert(sizeof(AnalogStick) == 0x8); + + struct PadStates { + s64_le sampling_number; + s64_le sampling_number2; + u32_le attribute; + u32_le button_state; + AnalogStick r_stick; + AnalogStick l_stick; + }; + static_assert(sizeof(PadStates) == 0x28, "PadStates is an invalid state"); + + struct SharedMemory { + CommonHeader header; + std::array<PadStates, 17> pad_states; + INSERT_PADDING_BYTES(0x138); + }; + static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size"); + SharedMemory shared_memory{}; +}; +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp new file mode 100644 index 000000000..898572277 --- /dev/null +++ b/src/core/hle/service/hid/controllers/gesture.cpp @@ -0,0 +1,43 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> +#include "common/common_types.h" +#include "core/core_timing.h" +#include "core/hle/service/hid/controllers/gesture.h" + +namespace Service::HID { +constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00; + +Controller_Gesture::Controller_Gesture() = default; +Controller_Gesture::~Controller_Gesture() = default; + +void Controller_Gesture::OnInit() {} + +void Controller_Gesture::OnRelease() {} + +void Controller_Gesture::OnUpdate(u8* data, std::size_t size) { + shared_memory.header.timestamp = CoreTiming::GetTicks(); + shared_memory.header.total_entry_count = 17; + + if (!IsControllerActivated()) { + shared_memory.header.entry_count = 0; + shared_memory.header.last_entry_index = 0; + return; + } + shared_memory.header.entry_count = 16; + + const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; + shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17; + auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; + + cur_entry.sampling_number = last_entry.sampling_number + 1; + cur_entry.sampling_number2 = cur_entry.sampling_number; + // TODO(ogniK): Update gesture states + + std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); +} + +void Controller_Gesture::OnLoadInputDevices() {} +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h new file mode 100644 index 000000000..1056ffbcd --- /dev/null +++ b/src/core/hle/service/hid/controllers/gesture.h @@ -0,0 +1,63 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include "common/common_types.h" +#include "common/swap.h" +#include "core/hle/service/hid/controllers/controller_base.h" + +namespace Service::HID { +class Controller_Gesture final : public ControllerBase { +public: + Controller_Gesture(); + ~Controller_Gesture() override; + + // Called when the controller is initialized + void OnInit() override; + + // When the controller is released + void OnRelease() override; + + // When the controller is requesting an update for the shared memory + void OnUpdate(u8* data, size_t size) override; + + // Called when input devices should be loaded + void OnLoadInputDevices() override; + +private: + struct Locations { + s32_le x; + s32_le y; + }; + + struct GestureState { + s64_le sampling_number; + s64_le sampling_number2; + + s64_le detection_count; + s32_le type; + s32_le dir; + s32_le x; + s32_le y; + s32_le delta_x; + s32_le delta_y; + f32 vel_x; + f32 vel_y; + s32_le attributes; + f32 scale; + f32 rotation; + s32_le location_count; + std::array<Locations, 4> locations; + }; + static_assert(sizeof(GestureState) == 0x68, "GestureState is an invalid size"); + + struct SharedMemory { + CommonHeader header; + std::array<GestureState, 17> gesture_states; + }; + SharedMemory shared_memory{}; +}; +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp new file mode 100644 index 000000000..ccfbce9ac --- /dev/null +++ b/src/core/hle/service/hid/controllers/keyboard.cpp @@ -0,0 +1,43 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> +#include "common/common_types.h" +#include "core/core_timing.h" +#include "core/hle/service/hid/controllers/keyboard.h" + +namespace Service::HID { +constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; + +Controller_Keyboard::Controller_Keyboard() = default; +Controller_Keyboard::~Controller_Keyboard() = default; + +void Controller_Keyboard::OnInit() {} + +void Controller_Keyboard::OnRelease() {} + +void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) { + shared_memory.header.timestamp = CoreTiming::GetTicks(); + shared_memory.header.total_entry_count = 17; + + if (!IsControllerActivated()) { + shared_memory.header.entry_count = 0; + shared_memory.header.last_entry_index = 0; + return; + } + shared_memory.header.entry_count = 16; + + const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index]; + shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17; + auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index]; + + cur_entry.sampling_number = last_entry.sampling_number + 1; + cur_entry.sampling_number2 = cur_entry.sampling_number; + // TODO(ogniK): Update keyboard states + + std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); +} + +void Controller_Keyboard::OnLoadInputDevices() {} +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h new file mode 100644 index 000000000..493e68fce --- /dev/null +++ b/src/core/hle/service/hid/controllers/keyboard.h @@ -0,0 +1,50 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/swap.h" +#include "core/hle/service/hid/controllers/controller_base.h" + +namespace Service::HID { +class Controller_Keyboard final : public ControllerBase { +public: + Controller_Keyboard(); + ~Controller_Keyboard() override; + + // Called when the controller is initialized + void OnInit() override; + + // When the controller is released + void OnRelease() override; + + // When the controller is requesting an update for the shared memory + void OnUpdate(u8* data, std::size_t size) override; + + // Called when input devices should be loaded + void OnLoadInputDevices() override; + +private: + struct KeyboardState { + s64_le sampling_number; + s64_le sampling_number2; + + s32_le modifier; + s32_le attribute; + std::array<u8, 32> key; + }; + static_assert(sizeof(KeyboardState) == 0x38, "KeyboardState is an invalid size"); + + struct SharedMemory { + CommonHeader header; + std::array<KeyboardState, 17> pad_states; + INSERT_PADDING_BYTES(0x28); + }; + static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size"); + SharedMemory shared_memory{}; +}; +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp new file mode 100644 index 000000000..4e246a57d --- /dev/null +++ b/src/core/hle/service/hid/controllers/mouse.cpp @@ -0,0 +1,43 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> +#include "common/common_types.h" +#include "core/core_timing.h" +#include "core/hle/service/hid/controllers/mouse.h" + +namespace Service::HID { +constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; + +Controller_Mouse::Controller_Mouse() = default; +Controller_Mouse::~Controller_Mouse() = default; + +void Controller_Mouse::OnInit() {} + +void Controller_Mouse::OnRelease() {} + +void Controller_Mouse::OnUpdate(u8* data, std::size_t size) { + shared_memory.header.timestamp = CoreTiming::GetTicks(); + shared_memory.header.total_entry_count = 17; + + if (!IsControllerActivated()) { + shared_memory.header.entry_count = 0; + shared_memory.header.last_entry_index = 0; + return; + } + shared_memory.header.entry_count = 16; + + auto& last_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index]; + shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17; + auto& cur_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index]; + + cur_entry.sampling_number = last_entry.sampling_number + 1; + cur_entry.sampling_number2 = cur_entry.sampling_number; + // TODO(ogniK): Update mouse states + + std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); +} + +void Controller_Mouse::OnLoadInputDevices() {} +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h new file mode 100644 index 000000000..543b0b71f --- /dev/null +++ b/src/core/hle/service/hid/controllers/mouse.h @@ -0,0 +1,50 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include "common/common_types.h" +#include "common/swap.h" +#include "core/hle/service/hid/controllers/controller_base.h" + +namespace Service::HID { +class Controller_Mouse final : public ControllerBase { +public: + Controller_Mouse(); + ~Controller_Mouse() override; + + // Called when the controller is initialized + void OnInit() override; + + // When the controller is released + void OnRelease() override; + + // When the controller is requesting an update for the shared memory + void OnUpdate(u8* data, std::size_t size) override; + + // Called when input devices should be loaded + void OnLoadInputDevices() override; + +private: + struct MouseState { + s64_le sampling_number; + s64_le sampling_number2; + s32_le x; + s32_le y; + s32_le delta_x; + s32_le delta_y; + s32_le mouse_wheel; + s32_le button; + s32_le attribute; + }; + static_assert(sizeof(MouseState) == 0x30, "MouseState is an invalid size"); + + struct SharedMemory { + CommonHeader header; + std::array<MouseState, 17> mouse_states; + }; + SharedMemory shared_memory{}; +}; +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp new file mode 100644 index 000000000..b06e65a77 --- /dev/null +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -0,0 +1,619 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <array> +#include <cstring> +#include "common/assert.h" +#include "common/bit_field.h" +#include "common/common_types.h" +#include "common/logging/log.h" +#include "core/core.h" +#include "core/core_timing.h" +#include "core/frontend/input.h" +#include "core/hle/kernel/event.h" +#include "core/hle/service/hid/controllers/npad.h" +#include "core/settings.h" + +namespace Service::HID { + +constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; +constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; +constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6; +constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E; +constexpr s32 HID_JOYSTICK_MAX = 0x7fff; +constexpr s32 HID_JOYSTICK_MIN = -0x7fff; +constexpr std::size_t NPAD_OFFSET = 0x9A00; +constexpr u32 BATTERY_FULL = 2; +constexpr u32 NPAD_HANDHELD = 32; +constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this? +constexpr u32 MAX_NPAD_ID = 7; +constexpr Controller_NPad::NPadControllerType PREFERRED_CONTROLLER = + Controller_NPad::NPadControllerType::JoyDual; +constexpr std::array<u32, 10> npad_id_list{ + 0, 1, 2, 3, 4, 5, 6, 7, 32, 16, +}; + +enum class JoystickId : std::size_t { + Joystick_Left, + Joystick_Right, +}; + +Controller_NPad::Controller_NPad() = default; +Controller_NPad::~Controller_NPad() = default; + +void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { + const auto controller_type = connected_controllers[controller_idx].type; + auto& controller = shared_memory_entries[controller_idx]; + if (controller_type == NPadControllerType::None) { + return; + } + controller.joy_styles.raw = 0; // Zero out + controller.device_type.raw = 0; + switch (controller_type) { + case NPadControllerType::Handheld: + controller.joy_styles.handheld.Assign(1); + controller.device_type.handheld.Assign(1); + controller.pad_assignment = NPadAssignments::Dual; + break; + case NPadControllerType::JoyDual: + controller.joy_styles.joycon_dual.Assign(1); + controller.device_type.joycon_left.Assign(1); + controller.device_type.joycon_right.Assign(1); + controller.pad_assignment = NPadAssignments::Dual; + break; + case NPadControllerType::JoyLeft: + controller.joy_styles.joycon_left.Assign(1); + controller.device_type.joycon_left.Assign(1); + controller.pad_assignment = NPadAssignments::Dual; + break; + case NPadControllerType::JoyRight: + controller.joy_styles.joycon_right.Assign(1); + controller.device_type.joycon_right.Assign(1); + controller.pad_assignment = NPadAssignments::Dual; + break; + case NPadControllerType::Pokeball: + controller.joy_styles.pokeball.Assign(1); + controller.device_type.pokeball.Assign(1); + controller.pad_assignment = NPadAssignments::Single; + break; + case NPadControllerType::ProController: + controller.joy_styles.pro_controller.Assign(1); + controller.device_type.pro_controller.Assign(1); + controller.pad_assignment = NPadAssignments::Single; + break; + } + + controller.single_color_error = ColorReadError::ReadOk; + controller.single_color.body_color = 0; + controller.single_color.button_color = 0; + + controller.dual_color_error = ColorReadError::ReadOk; + controller.left_color.body_color = JOYCON_BODY_NEON_BLUE; + controller.left_color.button_color = JOYCON_BUTTONS_NEON_BLUE; + controller.right_color.body_color = JOYCON_BODY_NEON_RED; + controller.right_color.button_color = JOYCON_BUTTONS_NEON_RED; + + controller.properties.is_vertical.Assign(1); // TODO(ogniK): Swap joycons orientations + controller.properties.use_plus.Assign(1); + controller.properties.use_minus.Assign(1); + controller.battery_level[0] = BATTERY_FULL; + controller.battery_level[1] = BATTERY_FULL; + controller.battery_level[2] = BATTERY_FULL; +} + +void Controller_NPad::OnInit() { + auto& kernel = Core::System::GetInstance().Kernel(); + styleset_changed_event = + Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged"); + + if (!IsControllerActivated()) + return; + std::size_t controller{}; + if (style.raw == 0) { + // We want to support all controllers + style.handheld.Assign(1); + style.joycon_left.Assign(1); + style.joycon_right.Assign(1); + style.joycon_dual.Assign(1); + style.pro_controller.Assign(1); + style.pokeball.Assign(1); + } + if (std::none_of(connected_controllers.begin(), connected_controllers.end(), + [](const ControllerHolder& controller) { return controller.is_connected; })) { + supported_npad_id_types.resize(npad_id_list.size()); + std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), + npad_id_list.size() * sizeof(u32)); + AddNewController(PREFERRED_CONTROLLER); + } +} + +void Controller_NPad::OnLoadInputDevices() { + std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, + Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END, + buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); + std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, + Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END, + sticks.begin(), Input::CreateDevice<Input::AnalogDevice>); +} + +void Controller_NPad::OnRelease() {} + +void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { + if (!IsControllerActivated()) + return; + for (std::size_t i = 0; i < shared_memory_entries.size(); i++) { + auto& npad = shared_memory_entries[i]; + const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states, + &npad.handheld_states, + &npad.dual_states, + &npad.left_joy_states, + &npad.right_joy_states, + &npad.pokeball_states, + &npad.libnx}; + + for (auto* main_controller : controller_npads) { + main_controller->common.entry_count = 16; + main_controller->common.total_entry_count = 17; + + const auto& last_entry = + main_controller->npad[main_controller->common.last_entry_index]; + + main_controller->common.timestamp = CoreTiming::GetTicks(); + main_controller->common.last_entry_index = + (main_controller->common.last_entry_index + 1) % 17; + + auto& cur_entry = main_controller->npad[main_controller->common.last_entry_index]; + + cur_entry.timestamp = last_entry.timestamp + 1; + cur_entry.timestamp2 = cur_entry.timestamp; + } + + const auto& controller_type = connected_controllers[i].type; + + if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) { + continue; + } + + // Pad states + ControllerPadState pad_state{}; + using namespace Settings::NativeButton; + pad_state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.l_stick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.r_stick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus()); + + pad_state.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus()); + + pad_state.l_stick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.l_stick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.l_stick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.l_stick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus()); + + pad_state.r_stick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.r_stick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.r_stick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.r_stick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus()); + + pad_state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus()); + + AnalogPosition lstick_entry{}; + AnalogPosition rstick_entry{}; + + const auto [stick_l_x_f, stick_l_y_f] = + sticks[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); + const auto [stick_r_x_f, stick_r_y_f] = + sticks[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); + lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); + lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); + rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); + rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); + + if (controller_type == NPadControllerType::JoyLeft || + controller_type == NPadControllerType::JoyRight) { + if (npad.properties.is_horizontal) { + ControllerPadState state{}; + AnalogPosition temp_lstick_entry{}; + AnalogPosition temp_rstick_entry{}; + if (controller_type == NPadControllerType::JoyLeft) { + state.d_down.Assign(pad_state.d_left.Value()); + state.d_left.Assign(pad_state.d_up.Value()); + state.d_right.Assign(pad_state.d_down.Value()); + state.d_up.Assign(pad_state.d_right.Value()); + state.l.Assign(pad_state.l.Value() | pad_state.sl.Value()); + state.r.Assign(pad_state.r.Value() | pad_state.sr.Value()); + + state.zl.Assign(pad_state.zl.Value()); + state.plus.Assign(pad_state.minus.Value()); + + temp_lstick_entry = lstick_entry; + temp_rstick_entry = rstick_entry; + std::swap(temp_lstick_entry.x, temp_lstick_entry.y); + std::swap(temp_rstick_entry.x, temp_rstick_entry.y); + temp_lstick_entry.y *= -1; + } else if (controller_type == NPadControllerType::JoyRight) { + state.x.Assign(pad_state.a.Value()); + state.a.Assign(pad_state.b.Value()); + state.b.Assign(pad_state.y.Value()); + state.y.Assign(pad_state.b.Value()); + + state.l.Assign(pad_state.l.Value() | pad_state.sl.Value()); + state.r.Assign(pad_state.r.Value() | pad_state.sr.Value()); + state.zr.Assign(pad_state.zr.Value()); + state.plus.Assign(pad_state.plus.Value()); + + temp_lstick_entry = lstick_entry; + temp_rstick_entry = rstick_entry; + std::swap(temp_lstick_entry.x, temp_lstick_entry.y); + std::swap(temp_rstick_entry.x, temp_rstick_entry.y); + temp_rstick_entry.x *= -1; + } + pad_state.raw = state.raw; + lstick_entry = temp_lstick_entry; + rstick_entry = temp_rstick_entry; + } + } + + auto& main_controller = + npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index]; + auto& handheld_entry = + npad.handheld_states.npad[npad.handheld_states.common.last_entry_index]; + auto& dual_entry = npad.dual_states.npad[npad.dual_states.common.last_entry_index]; + auto& left_entry = npad.left_joy_states.npad[npad.left_joy_states.common.last_entry_index]; + auto& right_entry = + npad.right_joy_states.npad[npad.right_joy_states.common.last_entry_index]; + auto& pokeball_entry = + npad.pokeball_states.npad[npad.pokeball_states.common.last_entry_index]; + auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index]; + + if (hold_type == NpadHoldType::Horizontal) { + // TODO(ogniK): Remap buttons for different orientations + } + libnx_entry.connection_status.raw = 0; + + switch (controller_type) { + case NPadControllerType::Handheld: + handheld_entry.connection_status.raw = 0; + handheld_entry.connection_status.IsConnected.Assign(1); + if (!Settings::values.use_docked_mode) { + handheld_entry.connection_status.IsWired.Assign(1); + } + handheld_entry.pad_states.raw = pad_state.raw; + handheld_entry.l_stick = lstick_entry; + handheld_entry.r_stick = rstick_entry; + break; + case NPadControllerType::JoyDual: + dual_entry.connection_status.raw = 0; + + dual_entry.connection_status.IsLeftJoyConnected.Assign(1); + dual_entry.connection_status.IsRightJoyConnected.Assign(1); + dual_entry.connection_status.IsConnected.Assign(1); + + libnx_entry.connection_status.IsLeftJoyConnected.Assign(1); + libnx_entry.connection_status.IsRightJoyConnected.Assign(1); + libnx_entry.connection_status.IsConnected.Assign(1); + + dual_entry.pad_states.raw = pad_state.raw; + dual_entry.l_stick = lstick_entry; + dual_entry.r_stick = rstick_entry; + case NPadControllerType::JoyLeft: + left_entry.connection_status.raw = 0; + + left_entry.connection_status.IsConnected.Assign(1); + left_entry.pad_states.raw = pad_state.raw; + left_entry.l_stick = lstick_entry; + left_entry.r_stick = rstick_entry; + break; + case NPadControllerType::JoyRight: + right_entry.connection_status.raw = 0; + + right_entry.connection_status.IsConnected.Assign(1); + right_entry.pad_states.raw = pad_state.raw; + right_entry.l_stick = lstick_entry; + right_entry.r_stick = rstick_entry; + break; + case NPadControllerType::Pokeball: + pokeball_entry.connection_status.raw = 0; + + pokeball_entry.connection_status.IsConnected.Assign(1); + pokeball_entry.connection_status.IsWired.Assign(1); + + pokeball_entry.pad_states.raw = pad_state.raw; + pokeball_entry.l_stick = lstick_entry; + pokeball_entry.r_stick = rstick_entry; + break; + case NPadControllerType::ProController: + main_controller.connection_status.raw = 0; + + main_controller.connection_status.IsConnected.Assign(1); + main_controller.connection_status.IsWired.Assign(1); + main_controller.pad_states.raw = pad_state.raw; + main_controller.l_stick = lstick_entry; + main_controller.r_stick = rstick_entry; + break; + } + + // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate + // any controllers. + libnx_entry.pad_states.raw = pad_state.raw; + libnx_entry.l_stick = lstick_entry; + libnx_entry.r_stick = rstick_entry; + } + std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(), + shared_memory_entries.size() * sizeof(NPadEntry)); +} // namespace Service::HID + +void Controller_NPad::SetSupportedStyleSet(NPadType style_set) { + style.raw = style_set.raw; +} + +Controller_NPad::NPadType Controller_NPad::GetSupportedStyleSet() const { + return style; +} + +void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) { + ASSERT(length > 0 && (length % sizeof(u32)) == 0); + supported_npad_id_types.clear(); + supported_npad_id_types.resize(length / sizeof(u32)); + std::memcpy(supported_npad_id_types.data(), data, length); + for (std::size_t i = 0; i < connected_controllers.size(); i++) { + auto& controller = connected_controllers[i]; + if (!controller.is_connected) { + continue; + } + if (!IsControllerSupported(PREFERRED_CONTROLLER)) { + controller.type = DecideBestController(PREFERRED_CONTROLLER); + InitNewlyAddedControler(i); + } + } +} + +void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { + ASSERT(max_length < supported_npad_id_types.size()); + std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size()); +} + +std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const { + return supported_npad_id_types.size(); +} + +void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) { + hold_type = joy_hold_type; +} +Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const { + return hold_type; +} + +void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) { + ASSERT(npad_id < shared_memory_entries.size()); + shared_memory_entries[npad_id].pad_assignment = assignment_mode; +} + +void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, + const std::vector<Vibration>& vibrations) { + if (!can_controllers_vibrate) { + return; + } + for (std::size_t i = 0; i < controller_ids.size(); i++) { + std::size_t controller_pos = i; + // Handheld controller conversion + if (controller_pos == NPAD_HANDHELD) { + controller_pos = 8; + } + // Unknown controller conversion + if (controller_pos == NPAD_UNKNOWN) { + controller_pos = 9; + } + if (connected_controllers[controller_pos].is_connected) { + // TODO(ogniK): Vibrate the physical controller + } + } + LOG_WARNING(Service_HID, "(STUBBED) called"); + last_processed_vibration = vibrations.back(); +} + +Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const { + return styleset_changed_event; +} + +Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { + return last_processed_vibration; +} +void Controller_NPad::AddNewController(NPadControllerType controller) { + if (controller == NPadControllerType::Handheld) { + connected_controllers[8] = {controller, true}; + InitNewlyAddedControler(8); + return; + } + const auto pos = + std::find_if(connected_controllers.begin(), connected_controllers.end() - 2, + [](const ControllerHolder& holder) { return !holder.is_connected; }); + if (pos == connected_controllers.end() - 2) { + LOG_ERROR(Service_HID, "Cannot connect any more controllers!"); + return; + } + const auto controller_id = std::distance(connected_controllers.begin(), pos); + connected_controllers[controller_id] = {controller, true}; + InitNewlyAddedControler(controller_id); +} + +void Controller_NPad::ConnectNPad(u32 npad_id) { + if (npad_id >= connected_controllers.size()) + return; + connected_controllers[npad_id].is_connected = true; +} + +void Controller_NPad::DisconnectNPad(u32 npad_id) { + if (npad_id >= connected_controllers.size()) + return; + connected_controllers[npad_id].is_connected = false; +} + +Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { + if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) { + // These are controllers without led patterns + return LedPattern{0, 0, 0, 0}; + } + switch (npad_id) { + case 0: + return LedPattern{1, 0, 0, 0}; + case 1: + return LedPattern{0, 1, 0, 0}; + case 2: + return LedPattern{0, 0, 1, 0}; + case 3: + return LedPattern{0, 0, 0, 1}; + case 4: + return LedPattern{1, 0, 0, 1}; + case 5: + return LedPattern{1, 0, 1, 0}; + case 6: + return LedPattern{1, 0, 1, 1}; + case 7: + return LedPattern{0, 1, 1, 0}; + default: + UNIMPLEMENTED_MSG("Unhandled npad_id {}", npad_id); + return LedPattern{0, 0, 0, 0}; + }; +} +void Controller_NPad::SetVibrationEnabled(bool can_vibrate) { + can_controllers_vibrate = can_vibrate; +} + +bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const { + const bool support_handheld = + std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) != + supported_npad_id_types.end(); + if (controller == NPadControllerType::Handheld) { + // Handheld is not even a supported type, lets stop here + if (!support_handheld) { + return false; + } + // Handheld should not be supported in docked mode + if (Settings::values.use_docked_mode) { + return false; + } + + return true; + } + if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(), + [](u32 npad_id) { return npad_id <= MAX_NPAD_ID; })) { + switch (controller) { + case NPadControllerType::ProController: + return style.pro_controller; + case NPadControllerType::JoyDual: + return style.joycon_dual; + case NPadControllerType::JoyLeft: + return style.joycon_left; + case NPadControllerType::JoyRight: + return style.joycon_right; + case NPadControllerType::Pokeball: + return style.pokeball; + default: + return false; + } + } + return false; +} + +Controller_NPad::NPadControllerType Controller_NPad::DecideBestController( + NPadControllerType priority) const { + if (IsControllerSupported(priority)) { + return priority; + } + const auto is_docked = Settings::values.use_docked_mode; + if (is_docked && priority == NPadControllerType::Handheld) { + priority = NPadControllerType::JoyDual; + if (IsControllerSupported(priority)) { + return priority; + } + } + std::vector<NPadControllerType> priority_list; + switch (priority) { + case NPadControllerType::ProController: + priority_list.push_back(NPadControllerType::JoyDual); + if (!is_docked) { + priority_list.push_back(NPadControllerType::Handheld); + } + priority_list.push_back(NPadControllerType::JoyLeft); + priority_list.push_back(NPadControllerType::JoyRight); + priority_list.push_back(NPadControllerType::Pokeball); + break; + case NPadControllerType::Handheld: + priority_list.push_back(NPadControllerType::JoyDual); + priority_list.push_back(NPadControllerType::ProController); + priority_list.push_back(NPadControllerType::JoyLeft); + priority_list.push_back(NPadControllerType::JoyRight); + priority_list.push_back(NPadControllerType::Pokeball); + break; + case NPadControllerType::JoyDual: + if (!is_docked) { + priority_list.push_back(NPadControllerType::Handheld); + } + priority_list.push_back(NPadControllerType::ProController); + priority_list.push_back(NPadControllerType::JoyLeft); + priority_list.push_back(NPadControllerType::JoyRight); + priority_list.push_back(NPadControllerType::Pokeball); + break; + case NPadControllerType::JoyLeft: + priority_list.push_back(NPadControllerType::JoyRight); + priority_list.push_back(NPadControllerType::JoyDual); + if (!is_docked) { + priority_list.push_back(NPadControllerType::Handheld); + } + priority_list.push_back(NPadControllerType::ProController); + priority_list.push_back(NPadControllerType::Pokeball); + break; + case NPadControllerType::JoyRight: + priority_list.push_back(NPadControllerType::JoyLeft); + priority_list.push_back(NPadControllerType::JoyDual); + if (!is_docked) { + priority_list.push_back(NPadControllerType::Handheld); + } + priority_list.push_back(NPadControllerType::ProController); + priority_list.push_back(NPadControllerType::Pokeball); + break; + case NPadControllerType::Pokeball: + priority_list.push_back(NPadControllerType::JoyLeft); + priority_list.push_back(NPadControllerType::JoyRight); + priority_list.push_back(NPadControllerType::JoyDual); + if (!is_docked) { + priority_list.push_back(NPadControllerType::Handheld); + } + priority_list.push_back(NPadControllerType::ProController); + break; + default: + priority_list.push_back(NPadControllerType::JoyDual); + if (!is_docked) { + priority_list.push_back(NPadControllerType::Handheld); + } + priority_list.push_back(NPadControllerType::ProController); + priority_list.push_back(NPadControllerType::JoyLeft); + priority_list.push_back(NPadControllerType::JoyRight); + priority_list.push_back(NPadControllerType::JoyDual); + } + + const auto iter = std::find_if(priority_list.begin(), priority_list.end(), + [this](auto type) { return IsControllerSupported(type); }); + if (iter == priority_list.end()) { + UNIMPLEMENTED_MSG("Could not find supported controller!"); + return priority; + } + + return *iter; +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h new file mode 100644 index 000000000..ac86985ff --- /dev/null +++ b/src/core/hle/service/hid/controllers/npad.h @@ -0,0 +1,289 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include "common/common_types.h" +#include "core/frontend/input.h" +#include "core/hle/service/hid/controllers/controller_base.h" +#include "core/settings.h" + +namespace Service::HID { + +class Controller_NPad final : public ControllerBase { +public: + Controller_NPad(); + ~Controller_NPad() override; + + // Called when the controller is initialized + void OnInit() override; + + // When the controller is released + void OnRelease() override; + + // When the controller is requesting an update for the shared memory + void OnUpdate(u8* data, std::size_t size) override; + + // Called when input devices should be loaded + void OnLoadInputDevices() override; + + struct NPadType { + union { + u32_le raw{}; + + BitField<0, 1, u32_le> pro_controller; + BitField<1, 1, u32_le> handheld; + BitField<2, 1, u32_le> joycon_dual; + BitField<3, 1, u32_le> joycon_left; + BitField<4, 1, u32_le> joycon_right; + + BitField<6, 1, u32_le> pokeball; // TODO(ogniK): Confirm when possible + }; + }; + static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size"); + + struct Vibration { + f32 amp_low; + f32 freq_low; + f32 amp_high; + f32 freq_high; + }; + static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size"); + + enum class NpadHoldType : u64 { + Vertical = 0, + Horizontal = 1, + }; + + enum class NPadAssignments : u32_le { + Dual = 0, + Single = 1, + }; + + enum class NPadControllerType { + None, + ProController, + Handheld, + JoyDual, + JoyLeft, + JoyRight, + Pokeball, + }; + + struct LedPattern { + explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) { + position1.Assign(light1); + position1.Assign(light2); + position1.Assign(light3); + position1.Assign(light4); + } + union { + u64 raw{}; + BitField<0, 1, u64> position1; + BitField<1, 1, u64> position2; + BitField<2, 1, u64> position3; + BitField<3, 1, u64> position4; + }; + }; + + void SetSupportedStyleSet(NPadType style_set); + NPadType GetSupportedStyleSet() const; + + void SetSupportedNPadIdTypes(u8* data, std::size_t length); + void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); + std::size_t GetSupportedNPadIdTypesSize() const; + + void SetHoldType(NpadHoldType joy_hold_type); + NpadHoldType GetHoldType() const; + + void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode); + + void VibrateController(const std::vector<u32>& controller_ids, + const std::vector<Vibration>& vibrations); + + Kernel::SharedPtr<Kernel::Event> GetStyleSetChangedEvent() const; + Vibration GetLastVibration() const; + + void AddNewController(NPadControllerType controller); + + void ConnectNPad(u32 npad_id); + void DisconnectNPad(u32 npad_id); + LedPattern GetLedPattern(u32 npad_id); + void SetVibrationEnabled(bool can_vibrate); + +private: + struct CommonHeader { + s64_le timestamp; + s64_le total_entry_count; + s64_le last_entry_index; + s64_le entry_count; + }; + static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size"); + + struct ControllerColor { + u32_le body_color; + u32_le button_color; + }; + static_assert(sizeof(ControllerColor) == 8, "ControllerColor is an invalid size"); + + struct ControllerPadState { + union { + u64_le raw{}; + // Button states + BitField<0, 1, u64_le> a; + BitField<1, 1, u64_le> b; + BitField<2, 1, u64_le> x; + BitField<3, 1, u64_le> y; + BitField<4, 1, u64_le> l_stick; + BitField<5, 1, u64_le> r_stick; + BitField<6, 1, u64_le> l; + BitField<7, 1, u64_le> r; + BitField<8, 1, u64_le> zl; + BitField<9, 1, u64_le> zr; + BitField<10, 1, u64_le> plus; + BitField<11, 1, u64_le> minus; + + // D-Pad + BitField<12, 1, u64_le> d_left; + BitField<13, 1, u64_le> d_up; + BitField<14, 1, u64_le> d_right; + BitField<15, 1, u64_le> d_down; + + // Left JoyStick + BitField<16, 1, u64_le> l_stick_left; + BitField<17, 1, u64_le> l_stick_up; + BitField<18, 1, u64_le> l_stick_right; + BitField<19, 1, u64_le> l_stick_down; + + // Right JoyStick + BitField<20, 1, u64_le> r_stick_left; + BitField<21, 1, u64_le> r_stick_up; + BitField<22, 1, u64_le> r_stick_right; + BitField<23, 1, u64_le> r_stick_down; + + // Not always active? + BitField<24, 1, u64_le> sl; + BitField<25, 1, u64_le> sr; + }; + }; + static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size"); + + struct AnalogPosition { + s32_le x; + s32_le y; + }; + static_assert(sizeof(AnalogPosition) == 8, "AnalogPosition is an invalid size"); + + struct ConnectionState { + union { + u32_le raw{}; + BitField<0, 1, u32_le> IsConnected; + BitField<1, 1, u32_le> IsWired; + BitField<2, 1, u32_le> IsLeftJoyConnected; + BitField<3, 1, u32_le> IsLeftJoyWired; + BitField<4, 1, u32_le> IsRightJoyConnected; + BitField<5, 1, u32_le> IsRightJoyWired; + }; + }; + static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size"); + + struct GenericStates { + s64_le timestamp; + s64_le timestamp2; + ControllerPadState pad_states; + AnalogPosition l_stick; + AnalogPosition r_stick; + ConnectionState connection_status; + }; + static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size"); + + struct NPadGeneric { + CommonHeader common; + std::array<GenericStates, 17> npad; + }; + static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size"); + + enum class ColorReadError : u32_le { + ReadOk = 0, + ColorDoesntExist = 1, + NoController = 2, + }; + + struct NPadProperties { + union { + s64_le raw{}; + BitField<11, 1, s64_le> is_vertical; + BitField<12, 1, s64_le> is_horizontal; + BitField<13, 1, s64_le> use_plus; + BitField<14, 1, s64_le> use_minus; + }; + }; + + struct NPadDevice { + union { + u32_le raw{}; + BitField<0, 1, s32_le> pro_controller; + BitField<1, 1, s32_le> handheld; + BitField<2, 1, s32_le> handheld_left; + BitField<3, 1, s32_le> handheld_right; + BitField<4, 1, s32_le> joycon_left; + BitField<5, 1, s32_le> joycon_right; + BitField<6, 1, s32_le> pokeball; + }; + }; + + struct NPadEntry { + NPadType joy_styles; + NPadAssignments pad_assignment; + + ColorReadError single_color_error; + ControllerColor single_color; + + ColorReadError dual_color_error; + ControllerColor left_color; + ControllerColor right_color; + + NPadGeneric main_controller_states; + NPadGeneric handheld_states; + NPadGeneric dual_states; + NPadGeneric left_joy_states; + NPadGeneric right_joy_states; + NPadGeneric pokeball_states; + NPadGeneric libnx; // TODO(ogniK): Find out what this actually is, libnx seems to only be + // relying on this for the time being + INSERT_PADDING_BYTES( + 0x708 * + 6); // TODO(ogniK): SixAxis states, require more information before implementation + NPadDevice device_type; + NPadProperties properties; + INSERT_PADDING_WORDS(1); + std::array<u32, 3> battery_level; + INSERT_PADDING_BYTES(0x5c); + INSERT_PADDING_BYTES(0xdf8); + }; + static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size"); + + struct ControllerHolder { + Controller_NPad::NPadControllerType type; + bool is_connected; + }; + + NPadType style{}; + std::array<NPadEntry, 10> shared_memory_entries{}; + std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> + buttons; + std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks; + std::vector<u32> supported_npad_id_types{}; + NpadHoldType hold_type{NpadHoldType::Vertical}; + Kernel::SharedPtr<Kernel::Event> styleset_changed_event; + Vibration last_processed_vibration{}; + std::array<ControllerHolder, 10> connected_controllers{}; + bool can_controllers_vibrate{true}; + + void InitNewlyAddedControler(std::size_t controller_idx); + bool IsControllerSupported(NPadControllerType controller) const; + NPadControllerType DecideBestController(NPadControllerType priority) const; +}; +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp new file mode 100644 index 000000000..02fcfadd9 --- /dev/null +++ b/src/core/hle/service/hid/controllers/stubbed.cpp @@ -0,0 +1,39 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> +#include "common/common_types.h" +#include "core/core_timing.h" +#include "core/hle/service/hid/controllers/stubbed.h" + +namespace Service::HID { + +Controller_Stubbed::Controller_Stubbed() = default; +Controller_Stubbed::~Controller_Stubbed() = default; + +void Controller_Stubbed::OnInit() {} + +void Controller_Stubbed::OnRelease() {} + +void Controller_Stubbed::OnUpdate(u8* data, std::size_t size) { + if (!smart_update) { + return; + } + + CommonHeader header{}; + header.timestamp = CoreTiming::GetTicks(); + header.total_entry_count = 17; + header.entry_count = 0; + header.last_entry_index = 0; + + std::memcpy(data + common_offset, &header, sizeof(CommonHeader)); +} + +void Controller_Stubbed::OnLoadInputDevices() {} + +void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) { + common_offset = off; + smart_update = true; +} +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h new file mode 100644 index 000000000..4a21c643e --- /dev/null +++ b/src/core/hle/service/hid/controllers/stubbed.h @@ -0,0 +1,34 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" +#include "core/hle/service/hid/controllers/controller_base.h" + +namespace Service::HID { +class Controller_Stubbed final : public ControllerBase { +public: + Controller_Stubbed(); + ~Controller_Stubbed() override; + + // Called when the controller is initialized + void OnInit() override; + + // When the controller is released + void OnRelease() override; + + // When the controller is requesting an update for the shared memory + void OnUpdate(u8* data, std::size_t size) override; + + // Called when input devices should be loaded + void OnLoadInputDevices() override; + + void SetCommonHeaderOffset(std::size_t off); + +private: + bool smart_update{}; + std::size_t common_offset{}; +}; +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp new file mode 100644 index 000000000..43efef803 --- /dev/null +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp @@ -0,0 +1,65 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> +#include "common/common_types.h" +#include "core/core_timing.h" +#include "core/frontend/emu_window.h" +#include "core/frontend/input.h" +#include "core/hle/service/hid/controllers/touchscreen.h" +#include "core/settings.h" + +namespace Service::HID { +constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; + +Controller_Touchscreen::Controller_Touchscreen() = default; +Controller_Touchscreen::~Controller_Touchscreen() = default; + +void Controller_Touchscreen::OnInit() {} + +void Controller_Touchscreen::OnRelease() {} + +void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) { + shared_memory.header.timestamp = CoreTiming::GetTicks(); + shared_memory.header.total_entry_count = 17; + + if (!IsControllerActivated()) { + shared_memory.header.entry_count = 0; + shared_memory.header.last_entry_index = 0; + return; + } + shared_memory.header.entry_count = 16; + + const auto& last_entry = + shared_memory.shared_memory_entries[shared_memory.header.last_entry_index]; + shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17; + auto& cur_entry = shared_memory.shared_memory_entries[shared_memory.header.last_entry_index]; + + cur_entry.sampling_number = last_entry.sampling_number + 1; + cur_entry.sampling_number2 = cur_entry.sampling_number; + + const auto [x, y, pressed] = touch_device->GetStatus(); + auto& touch_entry = cur_entry.states[0]; + if (pressed) { + touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width); + touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height); + touch_entry.diameter_x = 15; + touch_entry.diameter_y = 15; + touch_entry.rotation_angle = 0; + const u64 tick = CoreTiming::GetTicks(); + touch_entry.delta_time = tick - last_touch; + last_touch = tick; + touch_entry.finger = 0; + cur_entry.entry_count = 1; + } else { + cur_entry.entry_count = 0; + } + + std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory)); +} + +void Controller_Touchscreen::OnLoadInputDevices() { + touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device); +} +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h new file mode 100644 index 000000000..e5db6e6ba --- /dev/null +++ b/src/core/hle/service/hid/controllers/touchscreen.h @@ -0,0 +1,63 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/swap.h" +#include "core/frontend/input.h" +#include "core/hle/service/hid/controllers/controller_base.h" + +namespace Service::HID { +class Controller_Touchscreen final : public ControllerBase { +public: + Controller_Touchscreen(); + ~Controller_Touchscreen() override; + + // Called when the controller is initialized + void OnInit() override; + + // When the controller is released + void OnRelease() override; + + // When the controller is requesting an update for the shared memory + void OnUpdate(u8* data, std::size_t size) override; + + // Called when input devices should be loaded + void OnLoadInputDevices() override; + +private: + struct TouchState { + u64_le delta_time; + u32_le attribute; + u32_le finger; + u32_le x; + u32_le y; + u32_le diameter_x; + u32_le diameter_y; + u32_le rotation_angle; + }; + static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); + + struct TouchScreenEntry { + s64_le sampling_number; + s64_le sampling_number2; + s32_le entry_count; + std::array<TouchState, 16> states; + }; + static_assert(sizeof(TouchScreenEntry) == 0x298, "TouchScreenEntry is an invalid size"); + + struct TouchScreenSharedMemory { + CommonHeader header; + std::array<TouchScreenEntry, 17> shared_memory_entries{}; + INSERT_PADDING_BYTES(0x3c8); + }; + static_assert(sizeof(TouchScreenSharedMemory) == 0x3000, + "TouchScreenSharedMemory is an invalid size"); + TouchScreenSharedMemory shared_memory{}; + std::unique_ptr<Input::TouchDevice> touch_device; + s64_le last_touch{}; +}; +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp new file mode 100644 index 000000000..cd397c70b --- /dev/null +++ b/src/core/hle/service/hid/controllers/xpad.cpp @@ -0,0 +1,45 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> +#include "common/common_types.h" +#include "core/core_timing.h" +#include "core/hle/service/hid/controllers/xpad.h" + +namespace Service::HID { +constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; + +Controller_XPad::Controller_XPad() = default; +Controller_XPad::~Controller_XPad() = default; + +void Controller_XPad::OnInit() {} + +void Controller_XPad::OnRelease() {} + +void Controller_XPad::OnUpdate(u8* data, std::size_t size) { + for (auto& xpad_entry : shared_memory.shared_memory_entries) { + xpad_entry.header.timestamp = CoreTiming::GetTicks(); + xpad_entry.header.total_entry_count = 17; + + if (!IsControllerActivated()) { + xpad_entry.header.entry_count = 0; + xpad_entry.header.last_entry_index = 0; + return; + } + xpad_entry.header.entry_count = 16; + + const auto& last_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index]; + xpad_entry.header.last_entry_index = (xpad_entry.header.last_entry_index + 1) % 17; + auto& cur_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index]; + + cur_entry.sampling_number = last_entry.sampling_number + 1; + cur_entry.sampling_number2 = cur_entry.sampling_number; + } + // TODO(ogniK): Update xpad states + + std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); +} + +void Controller_XPad::OnLoadInputDevices() {} +} // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h new file mode 100644 index 000000000..ff836989f --- /dev/null +++ b/src/core/hle/service/hid/controllers/xpad.h @@ -0,0 +1,60 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/swap.h" +#include "core/hle/service/hid/controllers/controller_base.h" + +namespace Service::HID { +class Controller_XPad final : public ControllerBase { +public: + Controller_XPad(); + ~Controller_XPad() override; + + // Called when the controller is initialized + void OnInit() override; + + // When the controller is released + void OnRelease() override; + + // When the controller is requesting an update for the shared memory + void OnUpdate(u8* data, std::size_t size) override; + + // Called when input devices should be loaded + void OnLoadInputDevices() override; + +private: + struct AnalogStick { + s32_le x; + s32_le y; + }; + static_assert(sizeof(AnalogStick) == 0x8, "AnalogStick is an invalid size"); + + struct XPadState { + s64_le sampling_number; + s64_le sampling_number2; + s32_le attributes; + u32_le pad_states; + AnalogStick x_stick; + AnalogStick y_stick; + }; + static_assert(sizeof(XPadState) == 0x28, "XPadState is an invalid size"); + + struct XPadEntry { + CommonHeader header; + std::array<XPadState, 17> pad_states{}; + INSERT_PADDING_BYTES(0x138); + }; + static_assert(sizeof(XPadEntry) == 0x400, "XPadEntry is an invalid size"); + + struct SharedMemory { + std::array<XPadEntry, 4> shared_memory_entries{}; + }; + static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size"); + SharedMemory shared_memory{}; +}; +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 7c6b0a4e6..a9aa9ec78 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <array> +#include "common/common_types.h" #include "common/logging/log.h" #include "core/core.h" #include "core/core_timing.h" @@ -19,6 +21,16 @@ #include "core/hle/service/service.h" #include "core/settings.h" +#include "core/hle/service/hid/controllers/controller_base.h" +#include "core/hle/service/hid/controllers/debug_pad.h" +#include "core/hle/service/hid/controllers/gesture.h" +#include "core/hle/service/hid/controllers/keyboard.h" +#include "core/hle/service/hid/controllers/mouse.h" +#include "core/hle/service/hid/controllers/npad.h" +#include "core/hle/service/hid/controllers/stubbed.h" +#include "core/hle/service/hid/controllers/touchscreen.h" +#include "core/hle/service/hid/controllers/xpad.h" + namespace Service::HID { // Updating period for each HID device. @@ -26,6 +38,22 @@ namespace Service::HID { constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; +constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; +enum class HidController : std::size_t { + DebugPad, + Touchscreen, + Mouse, + Keyboard, + XPad, + Unknown1, + Unknown2, + Unknown3, + SixAxisSensor, + NPad, + Gesture, + + MaxControllers, +}; class IAppletResource final : public ServiceFramework<IAppletResource> { public: @@ -37,19 +65,57 @@ public: auto& kernel = Core::System::GetInstance().Kernel(); shared_mem = Kernel::SharedMemory::Create( - kernel, nullptr, 0x40000, Kernel::MemoryPermission::ReadWrite, + kernel, nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite, Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory"); + MakeController<Controller_DebugPad>(HidController::DebugPad); + MakeController<Controller_Touchscreen>(HidController::Touchscreen); + MakeController<Controller_Mouse>(HidController::Mouse); + MakeController<Controller_Keyboard>(HidController::Keyboard); + MakeController<Controller_XPad>(HidController::XPad); + MakeController<Controller_Stubbed>(HidController::Unknown1); + MakeController<Controller_Stubbed>(HidController::Unknown2); + MakeController<Controller_Stubbed>(HidController::Unknown3); + MakeController<Controller_Stubbed>(HidController::SixAxisSensor); + MakeController<Controller_NPad>(HidController::NPad); + MakeController<Controller_Gesture>(HidController::Gesture); + + // Homebrew doesn't try to activate some controllers, so we activate them by default + GetController<Controller_NPad>(HidController::NPad).ActivateController(); + GetController<Controller_Touchscreen>(HidController::Touchscreen).ActivateController(); + + GetController<Controller_Stubbed>(HidController::Unknown1).SetCommonHeaderOffset(0x4c00); + GetController<Controller_Stubbed>(HidController::Unknown2).SetCommonHeaderOffset(0x4e00); + GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000); + // Register update callbacks pad_update_event = CoreTiming::RegisterEvent( "HID::UpdatePadCallback", - [this](u64 userdata, int cycles_late) { UpdatePadCallback(userdata, cycles_late); }); + [this](u64 userdata, int cycles_late) { UpdateControllers(userdata, cycles_late); }); // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event); } + void ActivateController(HidController controller) { + controllers[static_cast<size_t>(controller)]->ActivateController(); + } + + void DeactivateController(HidController controller) { + controllers[static_cast<size_t>(controller)]->DeactivateController(); + } + + template <typename T> + void MakeController(HidController controller) { + controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(); + } + + template <typename T> + T& GetController(HidController controller) { + return static_cast<T&>(*controllers[static_cast<size_t>(controller)]); + } + ~IAppletResource() { CoreTiming::UnscheduleEvent(pad_update_event, 0); } @@ -62,200 +128,15 @@ private: LOG_DEBUG(Service_HID, "called"); } - void LoadInputDevices() { - std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, - Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END, - buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); - std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, - Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END, - sticks.begin(), Input::CreateDevice<Input::AnalogDevice>); - touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device); - // TODO(shinyquagsire23): gyro, mouse, keyboard - } - - void UpdatePadCallback(u64 userdata, int cycles_late) { - SharedMemory mem{}; - std::memcpy(&mem, shared_mem->GetPointer(), sizeof(SharedMemory)); - - if (Settings::values.is_device_reload_pending.exchange(false)) - LoadInputDevices(); - - // Set up controllers as neon red+blue Joy-Con attached to console - ControllerHeader& controller_header = mem.controllers[Controller_Handheld].header; - controller_header.type = ControllerType_Handheld; - controller_header.single_colors_descriptor = ColorDesc_ColorsNonexistent; - controller_header.right_color_body = JOYCON_BODY_NEON_RED; - controller_header.right_color_buttons = JOYCON_BUTTONS_NEON_RED; - controller_header.left_color_body = JOYCON_BODY_NEON_BLUE; - controller_header.left_color_buttons = JOYCON_BUTTONS_NEON_BLUE; - - for (std::size_t controller = 0; controller < mem.controllers.size(); controller++) { - for (auto& layout : mem.controllers[controller].layouts) { - layout.header.num_entries = HID_NUM_ENTRIES; - layout.header.max_entry_index = HID_NUM_ENTRIES - 1; - - // HID shared memory stores the state of the past 17 samples in a circlular buffer, - // each with a timestamp in number of samples since boot. - const ControllerInputEntry& last_entry = layout.entries[layout.header.latest_entry]; - - layout.header.timestamp_ticks = CoreTiming::GetTicks(); - layout.header.latest_entry = (layout.header.latest_entry + 1) % HID_NUM_ENTRIES; - - ControllerInputEntry& entry = layout.entries[layout.header.latest_entry]; - entry.timestamp = last_entry.timestamp + 1; - // TODO(shinyquagsire23): Is this always identical to timestamp? - entry.timestamp_2 = entry.timestamp; - - // TODO(shinyquagsire23): More than just handheld input - if (controller != Controller_Handheld) - continue; - - entry.connection_state = ConnectionState_Connected | ConnectionState_Wired; - - // TODO(shinyquagsire23): Set up some LUTs for each layout mapping in the future? - // For now everything is just the default handheld layout, but split Joy-Con will - // rotate the face buttons and directions for certain layouts. - ControllerPadState& state = entry.buttons; - using namespace Settings::NativeButton; - state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); - state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus()); - state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus()); - state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus()); - state.lstick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus()); - state.rstick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus()); - state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus()); - state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus()); - state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus()); - state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus()); - state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus()); - state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus()); - - state.dleft.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus()); - state.dup.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus()); - state.dright.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus()); - state.ddown.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus()); - - state.lstick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus()); - state.lstick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus()); - state.lstick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus()); - state.lstick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus()); - - state.rstick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus()); - state.rstick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus()); - state.rstick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus()); - state.rstick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus()); - - state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus()); - state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus()); - - const auto [stick_l_x_f, stick_l_y_f] = sticks[Joystick_Left]->GetStatus(); - const auto [stick_r_x_f, stick_r_y_f] = sticks[Joystick_Right]->GetStatus(); - entry.joystick_left_x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); - entry.joystick_left_y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); - entry.joystick_right_x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); - entry.joystick_right_y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); + void UpdateControllers(u64 userdata, int cycles_late) { + const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); + for (const auto& controller : controllers) { + if (should_reload) { + controller->OnLoadInputDevices(); } + controller->OnUpdate(shared_mem->GetPointer(), SHARED_MEMORY_SIZE); } - TouchScreen& touchscreen = mem.touchscreen; - const u64 last_entry = touchscreen.header.latest_entry; - const u64 curr_entry = (last_entry + 1) % touchscreen.entries.size(); - const u64 timestamp = CoreTiming::GetTicks(); - const u64 sample_counter = touchscreen.entries[last_entry].header.timestamp + 1; - touchscreen.header.timestamp_ticks = timestamp; - touchscreen.header.num_entries = touchscreen.entries.size(); - touchscreen.header.latest_entry = curr_entry; - touchscreen.header.max_entry_index = touchscreen.entries.size(); - touchscreen.header.timestamp = timestamp; - touchscreen.entries[curr_entry].header.timestamp = sample_counter; - - TouchScreenEntryTouch touch_entry{}; - auto [x, y, pressed] = touch_device->GetStatus(); - touch_entry.timestamp = timestamp; - touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width); - touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height); - touch_entry.touch_index = 0; - - // TODO(DarkLordZach): Maybe try to derive these from EmuWindow? - touch_entry.diameter_x = 15; - touch_entry.diameter_y = 15; - touch_entry.angle = 0; - - // TODO(DarkLordZach): Implement multi-touch support - if (pressed) { - touchscreen.entries[curr_entry].header.num_touches = 1; - touchscreen.entries[curr_entry].touches[0] = touch_entry; - } else { - touchscreen.entries[curr_entry].header.num_touches = 0; - } - - // TODO(shinyquagsire23): Properly implement mouse - Mouse& mouse = mem.mouse; - const u64 last_mouse_entry = mouse.header.latest_entry; - const u64 curr_mouse_entry = (mouse.header.latest_entry + 1) % mouse.entries.size(); - const u64 mouse_sample_counter = mouse.entries[last_mouse_entry].timestamp + 1; - mouse.header.timestamp_ticks = timestamp; - mouse.header.num_entries = mouse.entries.size(); - mouse.header.max_entry_index = mouse.entries.size(); - mouse.header.latest_entry = curr_mouse_entry; - - mouse.entries[curr_mouse_entry].timestamp = mouse_sample_counter; - mouse.entries[curr_mouse_entry].timestamp_2 = mouse_sample_counter; - - // TODO(shinyquagsire23): Properly implement keyboard - Keyboard& keyboard = mem.keyboard; - const u64 last_keyboard_entry = keyboard.header.latest_entry; - const u64 curr_keyboard_entry = - (keyboard.header.latest_entry + 1) % keyboard.entries.size(); - const u64 keyboard_sample_counter = keyboard.entries[last_keyboard_entry].timestamp + 1; - keyboard.header.timestamp_ticks = timestamp; - keyboard.header.num_entries = keyboard.entries.size(); - keyboard.header.latest_entry = last_keyboard_entry; - keyboard.header.max_entry_index = keyboard.entries.size(); - - keyboard.entries[curr_keyboard_entry].timestamp = keyboard_sample_counter; - keyboard.entries[curr_keyboard_entry].timestamp_2 = keyboard_sample_counter; - - // TODO(shinyquagsire23): Figure out what any of these are - for (auto& input : mem.unk_input_1) { - const u64 last_input_entry = input.header.latest_entry; - const u64 curr_input_entry = (input.header.latest_entry + 1) % input.entries.size(); - const u64 input_sample_counter = input.entries[last_input_entry].timestamp + 1; - - input.header.timestamp_ticks = timestamp; - input.header.num_entries = input.entries.size(); - input.header.latest_entry = last_input_entry; - input.header.max_entry_index = input.entries.size(); - - input.entries[curr_input_entry].timestamp = input_sample_counter; - input.entries[curr_input_entry].timestamp_2 = input_sample_counter; - } - - for (auto& input : mem.unk_input_2) { - input.header.timestamp_ticks = timestamp; - input.header.num_entries = 17; - input.header.latest_entry = 0; - input.header.max_entry_index = 0; - } - - UnkInput3& input = mem.unk_input_3; - const u64 last_input_entry = input.header.latest_entry; - const u64 curr_input_entry = (input.header.latest_entry + 1) % input.entries.size(); - const u64 input_sample_counter = input.entries[last_input_entry].timestamp + 1; - - input.header.timestamp_ticks = timestamp; - input.header.num_entries = input.entries.size(); - input.header.latest_entry = last_input_entry; - input.header.max_entry_index = input.entries.size(); - - input.entries[curr_input_entry].timestamp = input_sample_counter; - input.entries[curr_input_entry].timestamp_2 = input_sample_counter; - - // TODO(shinyquagsire23): Signal events - - std::memcpy(shared_mem->GetPointer(), &mem, sizeof(SharedMemory)); - - // Reschedule recurrent event CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); } @@ -265,11 +146,8 @@ private: // CoreTiming update events CoreTiming::EventType* pad_update_event; - // Stored input state info - std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> - buttons; - std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks; - std::unique_ptr<Input::TouchDevice> touch_device; + std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> + controllers{}; }; class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { @@ -299,9 +177,10 @@ public: {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"}, {21, &Hid::ActivateMouse, "ActivateMouse"}, {31, &Hid::ActivateKeyboard, "ActivateKeyboard"}, + {32, nullptr, "SendKeyboardLockKeyEvent"}, {40, nullptr, "AcquireXpadIdEventHandle"}, {41, nullptr, "ReleaseXpadIdEventHandle"}, - {51, nullptr, "ActivateXpad"}, + {51, &Hid::ActivateXpad, "ActivateXpad"}, {55, nullptr, "GetXpadIds"}, {56, nullptr, "ActivateJoyXpad"}, {58, nullptr, "GetJoyXpadLifoHandle"}, @@ -329,6 +208,7 @@ public: {80, nullptr, "GetGyroscopeZeroDriftMode"}, {81, nullptr, "ResetGyroscopeZeroDriftMode"}, {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, + {83, nullptr, "IsFirmwareUpdateAvailableForSixAxisSensor"}, {91, &Hid::ActivateGesture, "ActivateGesture"}, {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, @@ -362,8 +242,8 @@ public: {206, &Hid::SendVibrationValues, "SendVibrationValues"}, {207, nullptr, "SendVibrationGcErmCommand"}, {208, nullptr, "GetActualVibrationGcErmCommand"}, - {209, nullptr, "BeginPermitVibrationSession"}, - {210, nullptr, "EndPermitVibrationSession"}, + {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"}, + {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"}, {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, {302, nullptr, "StopConsoleSixAxisSensor"}, @@ -374,6 +254,7 @@ public: {307, nullptr, "FinalizeSevenSixAxisSensor"}, {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, + {310, nullptr, "ResetSevenSixAxisSensorTimestamp"}, {400, nullptr, "IsUsbFullKeyControllerEnabled"}, {401, nullptr, "EnableUsbFullKeyController"}, {402, nullptr, "IsUsbFullKeyControllerConnected"}, @@ -389,28 +270,35 @@ public: {505, nullptr, "SetPalmaFrModeType"}, {506, nullptr, "ReadPalmaStep"}, {507, nullptr, "EnablePalmaStep"}, - {508, nullptr, "SuspendPalmaStep"}, - {509, nullptr, "ResetPalmaStep"}, - {510, nullptr, "ReadPalmaApplicationSection"}, - {511, nullptr, "WritePalmaApplicationSection"}, - {512, nullptr, "ReadPalmaUniqueCode"}, - {513, nullptr, "SetPalmaUniqueCodeInvalid"}, + {508, nullptr, "ResetPalmaStep"}, + {509, nullptr, "ReadPalmaApplicationSection"}, + {510, nullptr, "WritePalmaApplicationSection"}, + {511, nullptr, "ReadPalmaUniqueCode"}, + {512, nullptr, "SetPalmaUniqueCodeInvalid"}, + {513, nullptr, "WritePalmaActivityEntry"}, + {514, nullptr, "WritePalmaRgbLedPatternEntry"}, + {515, nullptr, "WritePalmaWaveEntry"}, + {516, nullptr, "SetPalmaDataBaseIdentificationVersion"}, + {517, nullptr, "GetPalmaDataBaseIdentificationVersion"}, + {518, nullptr, "SuspendPalmaFeature"}, + {519, nullptr, "GetPalmaOperationResult"}, + {520, nullptr, "ReadPalmaPlayLog"}, + {521, nullptr, "ResetPalmaPlayLog"}, + {522, nullptr, "SetIsPalmaAllConnectable"}, + {523, nullptr, "SetIsPalmaPairedConnectable"}, + {524, nullptr, "PairPalma"}, + {525, nullptr, "SetPalmaBoostMode"}, {1000, nullptr, "SetNpadCommunicationMode"}, {1001, nullptr, "GetNpadCommunicationMode"}, }; // clang-format on RegisterHandlers(functions); - - auto& kernel = Core::System::GetInstance().Kernel(); - event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "hid:EventHandle"); } ~Hid() = default; private: std::shared_ptr<IAppletResource> applet_resource; - u32 joy_hold_type{0}; - Kernel::SharedPtr<Kernel::Event> event; void CreateAppletResource(Kernel::HLERequestContext& ctx) { if (applet_resource == nullptr) { @@ -423,31 +311,59 @@ private: LOG_DEBUG(Service_HID, "called"); } + void ActivateXpad(Kernel::HLERequestContext& ctx) { + applet_resource->ActivateController(HidController::XPad); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_DEBUG(Service_HID, "called"); + } + void ActivateDebugPad(Kernel::HLERequestContext& ctx) { + applet_resource->ActivateController(HidController::DebugPad); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_DEBUG(Service_HID, "called"); } void ActivateTouchScreen(Kernel::HLERequestContext& ctx) { + applet_resource->ActivateController(HidController::Touchscreen); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_DEBUG(Service_HID, "called"); } void ActivateMouse(Kernel::HLERequestContext& ctx) { + applet_resource->ActivateController(HidController::Mouse); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_DEBUG(Service_HID, "called"); } void ActivateKeyboard(Kernel::HLERequestContext& ctx) { + applet_resource->ActivateController(HidController::Keyboard); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_DEBUG(Service_HID, "called"); + } + + void ActivateGesture(Kernel::HLERequestContext& ctx) { + applet_resource->ActivateController(HidController::Gesture); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_DEBUG(Service_HID, "called"); + } + + void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) { + // Should have no effect with how our npad sets up the data + applet_resource->ActivateController(HidController::NPad); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_DEBUG(Service_HID, "called"); } void StartSixAxisSensor(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto handle = rp.PopRaw<u32>(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_HID, "(STUBBED) called"); @@ -468,84 +384,168 @@ private: } void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto supported_styleset = rp.PopRaw<u32>(); + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .SetSupportedStyleSet({supported_styleset}); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); + + LOG_DEBUG(Service_HID, "called"); } void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { + auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(0); - LOG_WARNING(Service_HID, "(STUBBED) called"); + rb.Push<u32>(controller.GetSupportedStyleSet().raw); + LOG_DEBUG(Service_HID, "called"); } void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) { + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .SetSupportedNPadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_DEBUG(Service_HID, "called"); } void ActivateNpad(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); + applet_resource->ActivateController(HidController::NPad); + LOG_DEBUG(Service_HID, "called"); } void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto npad_id = rp.PopRaw<u32>(); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(event); - LOG_WARNING(Service_HID, "(STUBBED) called"); + rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) + .GetStyleSetChangedEvent()); + LOG_DEBUG(Service_HID, "called"); } void DisconnectNpad(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto npad_id = rp.PopRaw<u32>(); + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .DisconnectNPad(npad_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_DEBUG(Service_HID, "called"); } void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2}; + IPC::RequestParser rp{ctx}; + auto npad_id = rp.PopRaw<u32>(); + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); + rb.PushRaw<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad) + .GetLedPattern(npad_id) + .raw); + LOG_DEBUG(Service_HID, "called"); } void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { + auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); + IPC::RequestParser rp{ctx}; + const auto hold_type = rp.PopRaw<u64>(); + controller.SetHoldType(Controller_NPad::NpadHoldType{hold_type}); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_DEBUG(Service_HID, "called"); } void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 3}; + const auto& controller = + applet_resource->GetController<Controller_NPad>(HidController::NPad); + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push(joy_hold_type); - LOG_WARNING(Service_HID, "(STUBBED) called"); + rb.Push<u64>(static_cast<u64>(controller.GetHoldType())); + LOG_DEBUG(Service_HID, "called"); } void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto npad_id = rp.PopRaw<u32>(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_HID, "(STUBBED) called"); } + void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) { + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .SetVibrationEnabled(true); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_DEBUG(Service_HID, "called"); + } + + void EndPermitVibrationSession(Kernel::HLERequestContext& ctx) { + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .SetVibrationEnabled(false); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_DEBUG(Service_HID, "called"); + } + void SendVibrationValue(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto controller_id = rp.PopRaw<u32>(); + const auto vibration_values = rp.PopRaw<Controller_NPad::Vibration>(); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); + + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .VibrateController({controller_id}, {vibration_values}); + LOG_DEBUG(Service_HID, "called"); } - void GetActualVibrationValue(Kernel::HLERequestContext& ctx) { + void SendVibrationValues(Kernel::HLERequestContext& ctx) { + const auto controllers = ctx.ReadBuffer(0); + const auto vibrations = ctx.ReadBuffer(1); + + std::vector<u32> controller_list(controllers.size() / sizeof(u32)); + std::vector<Controller_NPad::Vibration> vibration_list(vibrations.size() / + sizeof(Controller_NPad::Vibration)); + + std::memcpy(controller_list.data(), controllers.data(), controllers.size()); + std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size()); + std::transform(controller_list.begin(), controller_list.end(), controller_list.begin(), + [](u32 controller_id) { return controller_id - 3; }); + + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .VibrateController(controller_list, vibration_list); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_DEBUG(Service_HID, "called"); + } + + void GetActualVibrationValue(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw<Controller_NPad::Vibration>( + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .GetLastVibration()); + LOG_DEBUG(Service_HID, "called"); } void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id = rp.PopRaw<u32>(); + auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); + controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_DEBUG(Service_HID, "called"); } void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { @@ -555,6 +555,8 @@ private: } void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto mode = rp.PopRaw<u32>(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_HID, "(STUBBED) called"); @@ -563,8 +565,9 @@ private: void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push<u64>(0); - LOG_WARNING(Service_HID, "(STUBBED) called"); + rb.Push<u32>(1); + rb.Push<u32>(0); + LOG_DEBUG(Service_HID, "called"); } void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { @@ -574,12 +577,6 @@ private: LOG_DEBUG(Service_HID, "called"); } - void SendVibrationValues(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); - } - void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -597,18 +594,6 @@ private: rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_HID, "(STUBBED) called"); } - - void ActivateGesture(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); - } - - void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); - } }; class HidDbg final : public ServiceFramework<HidDbg> { @@ -650,6 +635,7 @@ public: {140, nullptr, "DeactivateConsoleSixAxisSensor"}, {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"}, {142, nullptr, "DeactivateSevenSixAxisSensor"}, + {143, nullptr, "GetConsoleSixAxisSensorCountStates"}, {201, nullptr, "ActivateFirmwareUpdate"}, {202, nullptr, "DeactivateFirmwareUpdate"}, {203, nullptr, "StartFirmwareUpdate"}, @@ -660,12 +646,23 @@ public: {208, nullptr, "StartFirmwareUpdateForRevert"}, {209, nullptr, "GetAvailableFirmwareVersionForRevert"}, {210, nullptr, "IsFirmwareUpdatingDevice"}, + {211, nullptr, "StartFirmwareUpdateIndividual"}, + {215, nullptr, "SetUsbFirmwareForceUpdateEnabled"}, + {216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"}, {221, nullptr, "UpdateControllerColor"}, {222, nullptr, "ConnectUsbPadsAsync"}, {223, nullptr, "DisconnectUsbPadsAsync"}, {224, nullptr, "UpdateDesignInfo"}, {225, nullptr, "GetUniquePadDriverState"}, {226, nullptr, "GetSixAxisSensorDriverStates"}, + {227, nullptr, "GetRxPacketHistory"}, + {228, nullptr, "AcquireOperationEventHandle"}, + {229, nullptr, "ReadSerialFlash"}, + {230, nullptr, "WriteSerialFlash"}, + {231, nullptr, "GetOperationResult"}, + {232, nullptr, "EnableShipmentMode"}, + {233, nullptr, "ClearPairingInfo"}, + {234, nullptr, "GetUniquePadDeviceTypeSetInternal"}, {301, nullptr, "GetAbstractedPadHandles"}, {302, nullptr, "GetAbstractedPadState"}, {303, nullptr, "GetAbstractedPadsState"}, @@ -673,6 +670,8 @@ public: {322, nullptr, "UnsetAutoPilotVirtualPadState"}, {323, nullptr, "UnsetAllAutoPilotVirtualPadState"}, {350, nullptr, "AddRegisteredDevice"}, + {400, nullptr, "DisableExternalMcuOnNxDevice"}, + {401, nullptr, "DisableRailDeviceFiltering"}, }; // clang-format on @@ -708,7 +707,9 @@ public: {307, nullptr, "GetNpadSystemExtStyle"}, {308, nullptr, "ApplyNpadSystemCommonPolicyFull"}, {309, nullptr, "GetNpadFullKeyGripColor"}, + {310, nullptr, "GetMaskedSupportedNpadStyleSet"}, {311, nullptr, "SetNpadPlayerLedBlinkingDevice"}, + {312, nullptr, "SetSupportedNpadStyleSetAll"}, {321, nullptr, "GetUniquePadsFromNpad"}, {322, nullptr, "GetIrSensorState"}, {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, @@ -733,6 +734,7 @@ public: {546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"}, {547, nullptr, "GetAllowedBluetoothLinksCount"}, {548, nullptr, "GetRegisteredDevices"}, + {549, nullptr, "GetConnectableRegisteredDevices"}, {700, nullptr, "ActivateUniquePad"}, {702, nullptr, "AcquireUniquePadConnectionEventHandle"}, {703, nullptr, "GetUniquePadIds"}, @@ -761,6 +763,7 @@ public: {850, nullptr, "IsUsbFullKeyControllerEnabled"}, {851, nullptr, "EnableUsbFullKeyController"}, {852, nullptr, "IsUsbConnected"}, + {870, nullptr, "IsHandheldButtonPressedOnConsoleMode"}, {900, nullptr, "ActivateInputDetector"}, {901, nullptr, "NotifyInputDetector"}, {1000, nullptr, "InitializeFirmwareUpdate"}, @@ -780,6 +783,12 @@ public: {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"}, {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"}, {1100, nullptr, "GetHidbusSystemServiceObject"}, + {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"}, + {1130, nullptr, "InitializeUsbFirmwareUpdate"}, + {1131, nullptr, "FinalizeUsbFirmwareUpdate"}, + {1132, nullptr, "CheckUsbFirmwareUpdateRequired"}, + {1133, nullptr, "StartUsbFirmwareUpdate"}, + {1134, nullptr, "GetUsbFirmwareUpdateState"}, }; // clang-format on @@ -818,6 +827,7 @@ public: {11, nullptr, "EnableJoyPollingReceiveMode"}, {12, nullptr, "DisableJoyPollingReceiveMode"}, {13, nullptr, "GetPollingData"}, + {14, nullptr, "SetStatusManagerType"}, }; // clang-format on diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 88d926808..773035460 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -4,408 +4,12 @@ #pragma once -#include <array> -#include "common/bit_field.h" -#include "common/common_types.h" -#include "core/hle/service/service.h" +namespace SM { +class ServiceManager; +} namespace Service::HID { -// Begin enums and output structs - -constexpr u32 HID_NUM_ENTRIES = 17; -constexpr u32 HID_NUM_LAYOUTS = 7; -constexpr s32 HID_JOYSTICK_MAX = 0x8000; -constexpr s32 HID_JOYSTICK_MIN = -0x8000; - -constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; -constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; -constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6; -constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E; - -enum ControllerType : u32 { - ControllerType_ProController = 1 << 0, - ControllerType_Handheld = 1 << 1, - ControllerType_JoyconPair = 1 << 2, - ControllerType_JoyconLeft = 1 << 3, - ControllerType_JoyconRight = 1 << 4, -}; - -enum ControllerLayoutType : u32 { - Layout_ProController = 0, // Pro Controller or HID gamepad - Layout_Handheld = 1, // Two Joy-Con docked to rails - Layout_Single = 2, // Horizontal single Joy-Con or pair of Joy-Con, adjusted for orientation - Layout_Left = 3, // Only raw left Joy-Con state, no orientation adjustment - Layout_Right = 4, // Only raw right Joy-Con state, no orientation adjustment - Layout_DefaultDigital = 5, // Same as next, but sticks have 8-direction values only - Layout_Default = 6, // Safe default, single Joy-Con have buttons/sticks rotated for orientation -}; - -enum ControllerColorDescription { - ColorDesc_ColorsNonexistent = 1 << 1, -}; - -enum ControllerConnectionState { - ConnectionState_Connected = 1 << 0, - ConnectionState_Wired = 1 << 1, -}; - -enum ControllerJoystick { - Joystick_Left = 0, - Joystick_Right = 1, -}; - -enum ControllerID { - Controller_Player1 = 0, - Controller_Player2 = 1, - Controller_Player3 = 2, - Controller_Player4 = 3, - Controller_Player5 = 4, - Controller_Player6 = 5, - Controller_Player7 = 6, - Controller_Player8 = 7, - Controller_Handheld = 8, - Controller_Unknown = 9, -}; - -// End enums and output structs - -// Begin UnkInput3 - -struct UnkInput3Header { - u64 timestamp_ticks; - u64 num_entries; - u64 latest_entry; - u64 max_entry_index; -}; -static_assert(sizeof(UnkInput3Header) == 0x20, "HID UnkInput3 header structure has incorrect size"); - -struct UnkInput3Entry { - u64 timestamp; - u64 timestamp_2; - u64 unk_8; - u64 unk_10; - u64 unk_18; -}; -static_assert(sizeof(UnkInput3Entry) == 0x28, "HID UnkInput3 entry structure has incorrect size"); - -struct UnkInput3 { - UnkInput3Header header; - std::array<UnkInput3Entry, 17> entries; - std::array<u8, 0x138> padding; -}; -static_assert(sizeof(UnkInput3) == 0x400, "HID UnkInput3 structure has incorrect size"); - -// End UnkInput3 - -// Begin TouchScreen - -struct TouchScreenHeader { - u64 timestamp_ticks; - u64 num_entries; - u64 latest_entry; - u64 max_entry_index; - u64 timestamp; -}; -static_assert(sizeof(TouchScreenHeader) == 0x28, - "HID touch screen header structure has incorrect size"); - -struct TouchScreenEntryHeader { - u64 timestamp; - u64 num_touches; -}; -static_assert(sizeof(TouchScreenEntryHeader) == 0x10, - "HID touch screen entry header structure has incorrect size"); - -struct TouchScreenEntryTouch { - u64 timestamp; - u32 padding; - u32 touch_index; - u32 x; - u32 y; - u32 diameter_x; - u32 diameter_y; - u32 angle; - u32 padding_2; -}; -static_assert(sizeof(TouchScreenEntryTouch) == 0x28, - "HID touch screen touch structure has incorrect size"); - -struct TouchScreenEntry { - TouchScreenEntryHeader header; - std::array<TouchScreenEntryTouch, 16> touches; - u64 unk; -}; -static_assert(sizeof(TouchScreenEntry) == 0x298, - "HID touch screen entry structure has incorrect size"); - -struct TouchScreen { - TouchScreenHeader header; - std::array<TouchScreenEntry, 17> entries; - std::array<u8, 0x3c0> padding; -}; -static_assert(sizeof(TouchScreen) == 0x3000, "HID touch screen structure has incorrect size"); - -// End TouchScreen - -// Begin Mouse - -struct MouseHeader { - u64 timestamp_ticks; - u64 num_entries; - u64 latest_entry; - u64 max_entry_index; -}; -static_assert(sizeof(MouseHeader) == 0x20, "HID mouse header structure has incorrect size"); - -struct MouseButtonState { - union { - u64 hex{}; - - // Buttons - BitField<0, 1, u64> left; - BitField<1, 1, u64> right; - BitField<2, 1, u64> middle; - BitField<3, 1, u64> forward; - BitField<4, 1, u64> back; - }; -}; - -struct MouseEntry { - u64 timestamp; - u64 timestamp_2; - u32 x; - u32 y; - u32 velocity_x; - u32 velocity_y; - u32 scroll_velocity_x; - u32 scroll_velocity_y; - MouseButtonState buttons; -}; -static_assert(sizeof(MouseEntry) == 0x30, "HID mouse entry structure has incorrect size"); - -struct Mouse { - MouseHeader header; - std::array<MouseEntry, 17> entries; - std::array<u8, 0xB0> padding; -}; -static_assert(sizeof(Mouse) == 0x400, "HID mouse structure has incorrect size"); - -// End Mouse - -// Begin Keyboard - -struct KeyboardHeader { - u64 timestamp_ticks; - u64 num_entries; - u64 latest_entry; - u64 max_entry_index; -}; -static_assert(sizeof(KeyboardHeader) == 0x20, "HID keyboard header structure has incorrect size"); - -struct KeyboardModifierKeyState { - union { - u64 hex{}; - - // Buttons - BitField<0, 1, u64> lctrl; - BitField<1, 1, u64> lshift; - BitField<2, 1, u64> lalt; - BitField<3, 1, u64> lmeta; - BitField<4, 1, u64> rctrl; - BitField<5, 1, u64> rshift; - BitField<6, 1, u64> ralt; - BitField<7, 1, u64> rmeta; - BitField<8, 1, u64> capslock; - BitField<9, 1, u64> scrolllock; - BitField<10, 1, u64> numlock; - }; -}; - -struct KeyboardEntry { - u64 timestamp; - u64 timestamp_2; - KeyboardModifierKeyState modifier; - u32 keys[8]; -}; -static_assert(sizeof(KeyboardEntry) == 0x38, "HID keyboard entry structure has incorrect size"); - -struct Keyboard { - KeyboardHeader header; - std::array<KeyboardEntry, 17> entries; - std::array<u8, 0x28> padding; -}; -static_assert(sizeof(Keyboard) == 0x400, "HID keyboard structure has incorrect size"); - -// End Keyboard - -// Begin UnkInput1 - -struct UnkInput1Header { - u64 timestamp_ticks; - u64 num_entries; - u64 latest_entry; - u64 max_entry_index; -}; -static_assert(sizeof(UnkInput1Header) == 0x20, "HID UnkInput1 header structure has incorrect size"); - -struct UnkInput1Entry { - u64 timestamp; - u64 timestamp_2; - u64 unk_8; - u64 unk_10; - u64 unk_18; -}; -static_assert(sizeof(UnkInput1Entry) == 0x28, "HID UnkInput1 entry structure has incorrect size"); - -struct UnkInput1 { - UnkInput1Header header; - std::array<UnkInput1Entry, 17> entries; - std::array<u8, 0x138> padding; -}; -static_assert(sizeof(UnkInput1) == 0x400, "HID UnkInput1 structure has incorrect size"); - -// End UnkInput1 - -// Begin UnkInput2 - -struct UnkInput2Header { - u64 timestamp_ticks; - u64 num_entries; - u64 latest_entry; - u64 max_entry_index; -}; -static_assert(sizeof(UnkInput2Header) == 0x20, "HID UnkInput2 header structure has incorrect size"); - -struct UnkInput2 { - UnkInput2Header header; - std::array<u8, 0x1E0> padding; -}; -static_assert(sizeof(UnkInput2) == 0x200, "HID UnkInput2 structure has incorrect size"); - -// End UnkInput2 - -// Begin Controller - -struct ControllerMAC { - u64 timestamp; - std::array<u8, 0x8> mac; - u64 unk; - u64 timestamp_2; -}; -static_assert(sizeof(ControllerMAC) == 0x20, "HID controller MAC structure has incorrect size"); - -struct ControllerHeader { - u32 type; - u32 is_half; - u32 single_colors_descriptor; - u32 single_color_body; - u32 single_color_buttons; - u32 split_colors_descriptor; - u32 left_color_body; - u32 left_color_buttons; - u32 right_color_body; - u32 right_color_buttons; -}; -static_assert(sizeof(ControllerHeader) == 0x28, - "HID controller header structure has incorrect size"); - -struct ControllerLayoutHeader { - u64 timestamp_ticks; - u64 num_entries; - u64 latest_entry; - u64 max_entry_index; -}; -static_assert(sizeof(ControllerLayoutHeader) == 0x20, - "HID controller layout header structure has incorrect size"); - -struct ControllerPadState { - union { - u64 hex{}; - - // Buttons - BitField<0, 1, u64> a; - BitField<1, 1, u64> b; - BitField<2, 1, u64> x; - BitField<3, 1, u64> y; - BitField<4, 1, u64> lstick; - BitField<5, 1, u64> rstick; - BitField<6, 1, u64> l; - BitField<7, 1, u64> r; - BitField<8, 1, u64> zl; - BitField<9, 1, u64> zr; - BitField<10, 1, u64> plus; - BitField<11, 1, u64> minus; - - // D-pad buttons - BitField<12, 1, u64> dleft; - BitField<13, 1, u64> dup; - BitField<14, 1, u64> dright; - BitField<15, 1, u64> ddown; - - // Left stick directions - BitField<16, 1, u64> lstick_left; - BitField<17, 1, u64> lstick_up; - BitField<18, 1, u64> lstick_right; - BitField<19, 1, u64> lstick_down; - - // Right stick directions - BitField<20, 1, u64> rstick_left; - BitField<21, 1, u64> rstick_up; - BitField<22, 1, u64> rstick_right; - BitField<23, 1, u64> rstick_down; - - BitField<24, 1, u64> sl; - BitField<25, 1, u64> sr; - }; -}; - -struct ControllerInputEntry { - u64 timestamp; - u64 timestamp_2; - ControllerPadState buttons; - s32 joystick_left_x; - s32 joystick_left_y; - s32 joystick_right_x; - s32 joystick_right_y; - u64 connection_state; -}; -static_assert(sizeof(ControllerInputEntry) == 0x30, - "HID controller input entry structure has incorrect size"); - -struct ControllerLayout { - ControllerLayoutHeader header; - std::array<ControllerInputEntry, 17> entries; -}; -static_assert(sizeof(ControllerLayout) == 0x350, - "HID controller layout structure has incorrect size"); - -struct Controller { - ControllerHeader header; - std::array<ControllerLayout, HID_NUM_LAYOUTS> layouts; - std::array<u8, 0x2a70> unk_1; - ControllerMAC mac_left; - ControllerMAC mac_right; - std::array<u8, 0xdf8> unk_2; -}; -static_assert(sizeof(Controller) == 0x5000, "HID controller structure has incorrect size"); - -// End Controller - -struct SharedMemory { - UnkInput3 unk_input_3; - TouchScreen touchscreen; - Mouse mouse; - Keyboard keyboard; - std::array<UnkInput1, 4> unk_input_1; - std::array<UnkInput2, 3> unk_input_2; - std::array<u8, 0x800> unk_section_8; - std::array<u8, 0x4000> controller_serials; - std::array<Controller, 10> controllers; - std::array<u8, 0x4600> unk_section_9; -}; -static_assert(sizeof(SharedMemory) == 0x40000, "HID Shared Memory structure has incorrect size"); - /// Reload input devices. Used when input configuration changed void ReloadInputDevices(); diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp index 7321584e1..164c57e18 100644 --- a/src/core/hle/service/lbl/lbl.cpp +++ b/src/core/hle/service/lbl/lbl.cpp @@ -18,35 +18,35 @@ public: explicit LBL() : ServiceFramework{"lbl"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "Unknown1"}, - {1, nullptr, "Unknown2"}, - {2, nullptr, "Unknown3"}, - {3, nullptr, "GetCurrentBacklightLevel"}, - {4, nullptr, "Unknown4"}, - {5, nullptr, "GetAlsComputedBacklightLevel"}, - {6, nullptr, "TurnOffBacklight"}, - {7, nullptr, "TurnOnBacklight"}, - {8, nullptr, "GetBacklightStatus"}, - {9, nullptr, "Unknown5"}, - {10, nullptr, "Unknown6"}, - {11, nullptr, "Unknown7"}, - {12, nullptr, "Unknown8"}, - {13, nullptr, "Unknown9"}, - {14, nullptr, "Unknown10"}, - {15, nullptr, "GetAutoBrightnessSetting"}, - {16, nullptr, "ReadRawLightSensor"}, - {17, nullptr, "Unknown11"}, - {18, nullptr, "Unknown12"}, - {19, nullptr, "Unknown13"}, - {20, nullptr, "Unknown14"}, - {21, nullptr, "Unknown15"}, - {22, nullptr, "Unknown16"}, - {23, nullptr, "Unknown17"}, - {24, nullptr, "Unknown18"}, - {25, nullptr, "Unknown19"}, + {0, nullptr, "SaveCurrentSetting"}, + {1, nullptr, "LoadCurrentSetting"}, + {2, nullptr, "SetCurrentBrightnessSetting"}, + {3, nullptr, "GetCurrentBrightnessSetting"}, + {4, nullptr, "ApplyCurrentBrightnessSettingToBacklight"}, + {5, nullptr, "GetBrightnessSettingAppliedToBacklight"}, + {6, nullptr, "SwitchBacklightOn"}, + {7, nullptr, "SwitchBacklightOff"}, + {8, nullptr, "GetBacklightSwitchStatus"}, + {9, nullptr, "EnableDimming"}, + {10, nullptr, "DisableDimming"}, + {11, nullptr, "IsDimmingEnabled"}, + {12, nullptr, "EnableAutoBrightnessControl"}, + {13, nullptr, "DisableAutoBrightnessControl"}, + {14, nullptr, "IsAutoBrightnessControlEnabled"}, + {15, nullptr, "SetAmbientLightSensorValue"}, + {16, nullptr, "GetAmbientLightSensorValue"}, + {17, nullptr, "SetBrightnessReflectionDelayLevel"}, + {18, nullptr, "GetBrightnessReflectionDelayLevel"}, + {19, nullptr, "SetCurrentBrightnessMapping"}, + {20, nullptr, "GetCurrentBrightnessMapping"}, + {21, nullptr, "SetCurrentAmbientLightSensorMapping"}, + {22, nullptr, "GetCurrentAmbientLightSensorMapping"}, + {23, nullptr, "IsAmbientLightSensorAvailable"}, + {24, nullptr, "SetCurrentBrightnessSettingForVrMode"}, + {25, nullptr, "GetCurrentBrightnessSettingForVrMode"}, {26, &LBL::EnableVrMode, "EnableVrMode"}, {27, &LBL::DisableVrMode, "DisableVrMode"}, - {28, &LBL::GetVrMode, "GetVrMode"}, + {28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"}, }; // clang-format on @@ -72,7 +72,7 @@ private: LOG_DEBUG(Service_LBL, "called"); } - void GetVrMode(Kernel::HLERequestContext& ctx) { + void IsVrModeEnabled(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(vr_mode_enabled); diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp index 7b91bb258..e1f17a926 100644 --- a/src/core/hle/service/mm/mm_u.cpp +++ b/src/core/hle/service/mm/mm_u.cpp @@ -14,14 +14,14 @@ public: explicit MM_U() : ServiceFramework{"mm:u"} { // clang-format off static const FunctionInfo functions[] = { - {0, &MM_U::Initialize, "InitializeOld"}, - {1, &MM_U::Finalize, "FinalizeOld"}, - {2, &MM_U::SetAndWait, "SetAndWaitOld"}, - {3, &MM_U::Get, "GetOld"}, - {4, &MM_U::Initialize, "Initialize"}, - {5, &MM_U::Finalize, "Finalize"}, - {6, &MM_U::SetAndWait, "SetAndWait"}, - {7, &MM_U::Get, "Get"}, + {0, &MM_U::Initialize, "Initialize"}, + {1, &MM_U::Finalize, "Finalize"}, + {2, &MM_U::SetAndWait, "SetAndWait"}, + {3, &MM_U::Get, "Get"}, + {4, &MM_U::InitializeWithId, "InitializeWithId"}, + {5, &MM_U::FinalizeWithId, "FinalizeWithId"}, + {6, &MM_U::SetAndWaitWithId, "SetAndWaitWithId"}, + {7, &MM_U::GetWithId, "GetWithId"}, }; // clang-format on @@ -59,9 +59,43 @@ private: rb.Push(current); } + void InitializeWithId(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_MM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(id); // Any non zero value + } + + void FinalizeWithId(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_MM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void SetAndWaitWithId(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + u32 input_id = rp.Pop<u32>(); + min = rp.Pop<u32>(); + max = rp.Pop<u32>(); + current = min; + + LOG_WARNING(Service_MM, "(STUBBED) called, input_id=0x{:X}, min=0x{:X}, max=0x{:X}", + input_id, min, max); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetWithId(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_MM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(current); + } + u32 min{0}; u32 max{0}; u32 current{0}; + u32 id{1}; }; void InstallInterfaces(SM::ServiceManager& service_manager) { diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 8c07a05c2..39c0c1e63 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -144,7 +144,7 @@ private: } const u64 device_handle{0xDEAD}; - const HID::ControllerID npad_id{HID::Controller_Player1}; + const u32 npad_id{0}; // This is the first player controller id State state{State::NonInitialized}; DeviceState device_state{DeviceState::Initialized}; Kernel::SharedPtr<Kernel::Event> activate_event; diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 10611ed6a..75dcd94a3 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -219,6 +219,7 @@ IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") { {35, nullptr, "GetScanData"}, {36, nullptr, "GetCurrentAccessPoint"}, {37, nullptr, "Shutdown"}, + {38, nullptr, "GetAllowedChannels"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index 261ad539c..18091c9bb 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp @@ -71,6 +71,22 @@ public: } }; +class NIM_ECA final : public ServiceFramework<NIM_ECA> { +public: + explicit NIM_ECA() : ServiceFramework{"nim:eca"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "CreateServerInterface"}, + {1, nullptr, "RefreshDebugAvailability"}, + {2, nullptr, "ClearDebugResponse"}, + {3, nullptr, "RegisterDebugResponse"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + class NIM_SHP final : public ServiceFramework<NIM_SHP> { public: explicit NIM_SHP() : ServiceFramework{"nim:shp"} { @@ -214,6 +230,7 @@ private: void InstallInterfaces(SM::ServiceManager& sm) { std::make_shared<NIM>()->InstallAsService(sm); + std::make_shared<NIM_ECA>()->InstallAsService(sm); std::make_shared<NIM_SHP>()->InstallAsService(sm); std::make_shared<NTC>()->InstallAsService(sm); } diff --git a/src/core/hle/service/npns/npns.cpp b/src/core/hle/service/npns/npns.cpp new file mode 100644 index 000000000..ccb6f9da9 --- /dev/null +++ b/src/core/hle/service/npns/npns.cpp @@ -0,0 +1,88 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "core/hle/service/npns/npns.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::NPNS { + +class NPNS_S final : public ServiceFramework<NPNS_S> { +public: + explicit NPNS_S() : ServiceFramework{"npns:s"} { + // clang-format off + static const FunctionInfo functions[] = { + {1, nullptr, "ListenAll"}, + {2, nullptr, "ListenTo"}, + {3, nullptr, "Receive"}, + {4, nullptr, "ReceiveRaw"}, + {5, nullptr, "GetReceiveEvent"}, + {6, nullptr, "ListenUndelivered"}, + {7, nullptr, "GetStateChangeEVent"}, + {11, nullptr, "SubscribeTopic"}, + {12, nullptr, "UnsubscribeTopic"}, + {13, nullptr, "QueryIsTopicExist"}, + {21, nullptr, "CreateToken"}, + {22, nullptr, "CreateTokenWithApplicationId"}, + {23, nullptr, "DestroyToken"}, + {24, nullptr, "DestroyTokenWithApplicationId"}, + {25, nullptr, "QueryIsTokenValid"}, + {31, nullptr, "UploadTokenToBaaS"}, + {32, nullptr, "DestroyTokenForBaaS"}, + {33, nullptr, "CreateTokenForBaaS"}, + {34, nullptr, "SetBaaSDeviceAccountIdList"}, + {101, nullptr, "Suspend"}, + {102, nullptr, "Resume"}, + {103, nullptr, "GetState"}, + {104, nullptr, "GetStatistics"}, + {105, nullptr, "GetPlayReportRequestEvent"}, + {111, nullptr, "GetJid"}, + {112, nullptr, "CreateJid"}, + {113, nullptr, "DestroyJid"}, + {114, nullptr, "AttachJid"}, + {115, nullptr, "DetachJid"}, + {201, nullptr, "RequestChangeStateForceTimed"}, + {102, nullptr, "RequestChangeStateForceAsync"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class NPNS_U final : public ServiceFramework<NPNS_U> { +public: + explicit NPNS_U() : ServiceFramework{"npns:u"} { + // clang-format off + static const FunctionInfo functions[] = { + {1, nullptr, "ListenAll"}, + {2, nullptr, "ListenTo"}, + {3, nullptr, "Receive"}, + {4, nullptr, "ReceiveRaw"}, + {5, nullptr, "GetReceiveEvent"}, + {7, nullptr, "GetStateChangeEVent"}, + {21, nullptr, "CreateToken"}, + {23, nullptr, "DestroyToken"}, + {25, nullptr, "QueryIsTokenValid"}, + {26, nullptr, "ListenToMyApplicationId"}, + {101, nullptr, "Suspend"}, + {102, nullptr, "Resume"}, + {103, nullptr, "GetState"}, + {104, nullptr, "GetStatistics"}, + {111, nullptr, "GetJid"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<NPNS_S>()->InstallAsService(sm); + std::make_shared<NPNS_U>()->InstallAsService(sm); +} + +} // namespace Service::NPNS diff --git a/src/core/hle/service/npns/npns.h b/src/core/hle/service/npns/npns.h new file mode 100644 index 000000000..861cd3e48 --- /dev/null +++ b/src/core/hle/service/npns/npns.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::NPNS { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::NPNS diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 98017267c..07c1381fe 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -93,13 +93,23 @@ public: {86, nullptr, "EnableApplicationCrashReport"}, {87, nullptr, "IsApplicationCrashReportEnabled"}, {90, nullptr, "BoostSystemMemoryResourceLimit"}, + {91, nullptr, "Unknown1"}, + {92, nullptr, "Unknown2"}, + {93, nullptr, "GetMainApplicationProgramIndex"}, + {94, nullptr, "LaunchApplication2"}, + {95, nullptr, "GetApplicationLaunchInfo"}, + {96, nullptr, "AcquireApplicationLaunchInfo"}, + {97, nullptr, "GetMainApplicationProgramIndex2"}, + {98, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, {100, nullptr, "ResetToFactorySettings"}, {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, {102, nullptr, "ResetToFactorySettingsForRefurbishment"}, {200, nullptr, "CalculateUserSaveDataStatistics"}, {201, nullptr, "DeleteUserSaveDataAll"}, {210, nullptr, "DeleteUserSystemSaveData"}, + {211, nullptr, "DeleteSaveData"}, {220, nullptr, "UnregisterNetworkServiceAccount"}, + {221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"}, {300, nullptr, "GetApplicationShellEvent"}, {301, nullptr, "PopApplicationShellEventInfo"}, {302, nullptr, "LaunchLibraryApplet"}, @@ -114,6 +124,7 @@ public: {403, nullptr, "GetMaxApplicationControlCacheCount"}, {404, nullptr, "InvalidateApplicationControlCache"}, {405, nullptr, "ListApplicationControlCacheEntryInfo"}, + {406, nullptr, "GetApplicationControlProperty"}, {502, nullptr, "RequestCheckGameCardRegistration"}, {503, nullptr, "RequestGameCardRegistrationGoldPoint"}, {504, nullptr, "RequestRegisterGameCard"}, @@ -129,6 +140,7 @@ public: {604, nullptr, "RegisterContentsExternalKey"}, {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"}, {606, nullptr, "GetContentMetaStorage"}, + {607, nullptr, "ListAvailableAddOnContent"}, {700, nullptr, "PushDownloadTaskList"}, {701, nullptr, "ClearTaskStatusList"}, {702, nullptr, "RequestDownloadTaskList"}, @@ -148,6 +160,9 @@ public: {907, nullptr, "WithdrawApplicationUpdateRequest"}, {908, nullptr, "ListApplicationRecordInstalledContentMeta"}, {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"}, + {910, nullptr, "Unknown3"}, + {911, nullptr, "SetPreInstalledApplication"}, + {912, nullptr, "ClearPreInstalledApplicationFlag"}, {1000, nullptr, "RequestVerifyApplicationDeprecated"}, {1001, nullptr, "CorruptApplicationForDebug"}, {1002, nullptr, "RequestVerifyAddOnContentsRights"}, @@ -162,6 +177,8 @@ public: {1305, nullptr, "TryDeleteRunningApplicationEntity"}, {1306, nullptr, "TryDeleteRunningApplicationCompletely"}, {1307, nullptr, "TryDeleteRunningApplicationContentEntities"}, + {1308, nullptr, "DeleteApplicationCompletelyForDebug"}, + {1309, nullptr, "CleanupUnavailableAddOnContents"}, {1400, nullptr, "PrepareShutdown"}, {1500, nullptr, "FormatSdCard"}, {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"}, @@ -199,6 +216,28 @@ public: {2015, nullptr, "CompareSystemDeliveryInfo"}, {2016, nullptr, "ListNotCommittedContentMeta"}, {2017, nullptr, "CreateDownloadTask"}, + {2018, nullptr, "Unknown4"}, + {2050, nullptr, "Unknown5"}, + {2100, nullptr, "Unknown6"}, + {2101, nullptr, "Unknown7"}, + {2150, nullptr, "CreateRightsEnvironment"}, + {2151, nullptr, "DestroyRightsEnvironment"}, + {2152, nullptr, "ActivateRightsEnvironment"}, + {2153, nullptr, "DeactivateRightsEnvironment"}, + {2154, nullptr, "ForceActivateRightsContextForExit"}, + {2160, nullptr, "AddTargetApplicationToRightsEnvironment"}, + {2161, nullptr, "SetUsersToRightsEnvironment"}, + {2170, nullptr, "GetRightsEnvironmentStatus"}, + {2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"}, + {2180, nullptr, "RequestExtendRightsInRightsEnvironment"}, + {2181, nullptr, "GetLastResultOfExtendRightsInRightsEnvironment"}, + {2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"}, + {2190, nullptr, "GetRightsEnvironmentHandleForApplication"}, + {2199, nullptr, "GetRightsEnvironmentCountForDebug"}, + {2200, nullptr, "Unknown8"}, + {2201, nullptr, "Unknown9"}, + {2250, nullptr, "Unknown10"}, + {2300, nullptr, "Unknown11"}, }; // clang-format on @@ -348,12 +387,15 @@ public: {0, nullptr, "LaunchProgram"}, {1, nullptr, "TerminateProcess"}, {2, nullptr, "TerminateProgram"}, - {3, nullptr, "GetShellEventHandle"}, - {4, nullptr, "GetShellEventInfo"}, - {5, nullptr, "TerminateApplication"}, - {6, nullptr, "PrepareLaunchProgramFromHost"}, - {7, nullptr, "LaunchApplication"}, - {8, nullptr, "LaunchApplicationWithStorageId"}, + {4, nullptr, "GetShellEventHandle"}, + {5, nullptr, "GetShellEventInfo"}, + {6, nullptr, "TerminateApplication"}, + {7, nullptr, "PrepareLaunchProgramFromHost"}, + {8, nullptr, "LaunchApplication"}, + {9, nullptr, "LaunchApplicationWithStorageId"}, + {10, nullptr, "TerminateApplication2"}, + {11, nullptr, "GetRunningApplicationProcessId"}, + {12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActive"}, }; // clang-format on @@ -388,6 +430,7 @@ public: {19, nullptr, "GetReceivedEulaDataSize"}, {20, nullptr, "GetReceivedEulaData"}, {21, nullptr, "SetupToReceiveSystemUpdate"}, + {22, nullptr, "RequestCheckLatestUpdateIncludesRebootlessUpdate"}, }; // clang-format on diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index 6a9eccfb5..e4fcee9f8 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -14,20 +14,24 @@ public: explicit PlayReport(const char* name) : ServiceFramework{name} { // clang-format off static const FunctionInfo functions[] = { - {10100, nullptr, "SaveReport"}, - {10101, &PlayReport::SaveReportWithUser, "SaveReportWithUser"}, + {10100, nullptr, "SaveReportOld"}, + {10101, &PlayReport::SaveReportWithUserOld, "SaveReportWithUserOld"}, + {10102, nullptr, "SaveReport"}, + {10103, nullptr, "SaveReportWithUser"}, {10200, nullptr, "RequestImmediateTransmission"}, {10300, nullptr, "GetTransmissionStatus"}, {20100, nullptr, "SaveSystemReport"}, - {20200, nullptr, "SetOperationMode"}, {20101, nullptr, "SaveSystemReportWithUser"}, + {20200, nullptr, "SetOperationMode"}, {30100, nullptr, "ClearStorage"}, + {30200, nullptr, "ClearStatistics"}, + {30300, nullptr, "GetStorageUsage"}, + {30400, nullptr, "GetStatistics"}, + {30401, nullptr, "GetThroughputHistory"}, + {30500, nullptr, "GetLastUploadError"}, {40100, nullptr, "IsUserAgreementCheckEnabled"}, {40101, nullptr, "SetUserAgreementCheckEnabled"}, - {90100, nullptr, "GetStorageUsage"}, - {90200, nullptr, "GetStatistics"}, - {90201, nullptr, "GetThroughputHistory"}, - {90300, nullptr, "GetLastUploadError"}, + {90100, nullptr, "ReadAllReportFiles"}, }; // clang-format on @@ -35,7 +39,7 @@ public: } private: - void SaveReportWithUser(Kernel::HLERequestContext& ctx) { + void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) { // TODO(ogniK): Do we want to add play report? LOG_WARNING(Service_PREPO, "(STUBBED) called"); @@ -46,6 +50,7 @@ private: void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<PlayReport>("prepo:a")->InstallAsService(service_manager); + std::make_shared<PlayReport>("prepo:a2")->InstallAsService(service_manager); std::make_shared<PlayReport>("prepo:m")->InstallAsService(service_manager); std::make_shared<PlayReport>("prepo:s")->InstallAsService(service_manager); std::make_shared<PlayReport>("prepo:u")->InstallAsService(service_manager); diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp new file mode 100644 index 000000000..c2d5fda94 --- /dev/null +++ b/src/core/hle/service/ptm/psm.cpp @@ -0,0 +1,71 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/ptm/psm.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::PSM { + +constexpr u32 BATTERY_FULLY_CHARGED = 100; // 100% Full +constexpr u32 BATTERY_CURRENTLY_CHARGING = 1; // Plugged into an official dock + +class PSM final : public ServiceFramework<PSM> { +public: + explicit PSM() : ServiceFramework{"psm"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"}, + {1, &PSM::GetChargerType, "GetChargerType"}, + {2, nullptr, "EnableBatteryCharging"}, + {3, nullptr, "DisableBatteryCharging"}, + {4, nullptr, "IsBatteryChargingEnabled"}, + {5, nullptr, "AcquireControllerPowerSupply"}, + {6, nullptr, "ReleaseControllerPowerSupply"}, + {7, nullptr, "OpenSession"}, + {8, nullptr, "EnableEnoughPowerChargeEmulation"}, + {9, nullptr, "DisableEnoughPowerChargeEmulation"}, + {10, nullptr, "EnableFastBatteryCharging"}, + {11, nullptr, "DisableFastBatteryCharging"}, + {12, nullptr, "GetBatteryVoltageState"}, + {13, nullptr, "GetRawBatteryChargePercentage"}, + {14, nullptr, "IsEnoughPowerSupplied"}, + {15, nullptr, "GetBatteryAgePercentage"}, + {16, nullptr, "GetBatteryChargeInfoEvent"}, + {17, nullptr, "GetBatteryChargeInfoFields"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + + ~PSM() override = default; + +private: + void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_PSM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(BATTERY_FULLY_CHARGED); + } + + void GetChargerType(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_PSM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(BATTERY_CURRENTLY_CHARGING); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<PSM>()->InstallAsService(sm); +} + +} // namespace Service::PSM diff --git a/src/core/hle/service/ptm/psm.h b/src/core/hle/service/ptm/psm.h new file mode 100644 index 000000000..a286793ae --- /dev/null +++ b/src/core/hle/service/ptm/psm.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::PSM { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::PSM diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index a225cb4cb..a4cf45267 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -22,7 +22,7 @@ #include "core/hle/service/apm/apm.h" #include "core/hle/service/arp/arp.h" #include "core/hle/service/audio/audio.h" -#include "core/hle/service/bcat/bcat.h" +#include "core/hle/service/bcat/module.h" #include "core/hle/service/bpc/bpc.h" #include "core/hle/service/btdrv/btdrv.h" #include "core/hle/service/btm/btm.h" @@ -48,15 +48,17 @@ #include "core/hle/service/nfp/nfp.h" #include "core/hle/service/nifm/nifm.h" #include "core/hle/service/nim/nim.h" +#include "core/hle/service/npns/npns.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvflinger/nvflinger.h" #include "core/hle/service/pcie/pcie.h" -#include "core/hle/service/pctl/pctl.h" +#include "core/hle/service/pctl/module.h" #include "core/hle/service/pcv/pcv.h" #include "core/hle/service/pm/pm.h" #include "core/hle/service/prepo/prepo.h" #include "core/hle/service/psc/psc.h" +#include "core/hle/service/ptm/psm.h" #include "core/hle/service/service.h" #include "core/hle/service/set/settings.h" #include "core/hle/service/sm/sm.h" @@ -236,6 +238,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs) NFP::InstallInterfaces(*sm); NIFM::InstallInterfaces(*sm); NIM::InstallInterfaces(*sm); + NPNS::InstallInterfaces(*sm); NS::InstallInterfaces(*sm); Nvidia::InstallInterfaces(*sm, *nv_flinger); PCIe::InstallInterfaces(*sm); @@ -244,6 +247,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs) PlayReport::InstallInterfaces(*sm); PM::InstallInterfaces(*sm); PSC::InstallInterfaces(*sm); + PSM::InstallInterfaces(*sm); Set::InstallInterfaces(*sm); Sockets::InstallInterfaces(*sm); SPL::InstallInterfaces(*sm); diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp index 5af356d10..34654bb07 100644 --- a/src/core/hle/service/set/set_cal.cpp +++ b/src/core/hle/service/set/set_cal.cpp @@ -39,7 +39,8 @@ SET_CAL::SET_CAL() : ServiceFramework("set:cal") { {29, nullptr, "GetAmiiboEcqvBlsKey"}, {30, nullptr, "GetAmiiboEcqvBlsCertificate"}, {31, nullptr, "GetAmiiboEcqvBlsRootCertificate"}, - {32, nullptr, "GetUnknownId"}, + {32, nullptr, "GetUsbTypeCPowerSourceCircuitVersion"}, + {33, nullptr, "GetBatteryVersion"}, }; RegisterHandlers(functions); } diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index 7a619acb4..461607c95 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp @@ -59,8 +59,7 @@ ResultStatus AppLoader_XCI::Load(Kernel::Process& process) { if (xci->GetProgramNCAStatus() != ResultStatus::Success) return xci->GetProgramNCAStatus(); - const auto nca = xci->GetProgramNCA(); - if (nca == nullptr && !Core::Crypto::KeyManager::KeyFileExists(false)) + if (!xci->HasProgramNCA() && !Core::Crypto::KeyManager::KeyFileExists(false)) return ResultStatus::ErrorMissingProductionKeyFile; const auto result = nca_loader->Load(process); diff --git a/src/core/settings.h b/src/core/settings.h index 83b9a04c8..8f2da01c8 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -136,7 +136,7 @@ struct Values { float resolution_factor; bool use_frame_limit; u16 frame_limit; - bool use_accurate_framebuffers; + bool use_accurate_gpu_emulation; float bg_red; float bg_green; diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 7b04792b5..0de13edd3 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -163,8 +163,8 @@ TelemetrySession::TelemetrySession() { AddField(Telemetry::FieldType::UserConfig, "Renderer_UseFrameLimit", Settings::values.use_frame_limit); AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", Settings::values.frame_limit); - AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateFramebuffers", - Settings::values.use_accurate_framebuffers); + AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation", + Settings::values.use_accurate_gpu_emulation); AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode", Settings::values.use_docked_mode); } |