diff options
Diffstat (limited to 'src')
53 files changed, 777 insertions, 387 deletions
diff --git a/src/common/alignment.h b/src/common/alignment.h index 225770fab..d94a2291f 100644 --- a/src/common/alignment.h +++ b/src/common/alignment.h @@ -19,4 +19,16 @@ constexpr T AlignDown(T value, std::size_t size) { return static_cast<T>(value - value % size); } +template <typename T> +constexpr bool Is4KBAligned(T value) { + static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); + return (value & 0xFFF) == 0; +} + +template <typename T> +constexpr bool IsWordAligned(T value) { + static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); + return (value & 0b11) == 0; +} + } // namespace Common 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/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index d3971a25e..3b8a2e230 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; } @@ -352,6 +350,10 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, return ERR_INVALID_ADDRESS_STATE; } + if (!Common::IsWordAligned(mutex_addr)) { + return ERR_INVALID_ADDRESS; + } + auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle, requesting_thread_handle); @@ -365,6 +367,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); } @@ -570,11 +576,11 @@ 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; } @@ -609,11 +615,11 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 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; } diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 69bfce1c1..4d1f83170 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -638,10 +638,12 @@ 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"}, @@ -669,6 +671,32 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF 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 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/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/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 index 1625e9c3d..0993a7815 100644 --- a/src/core/hle/service/hid/controllers/controller_base.cpp +++ b/src/core/hle/service/hid/controllers/controller_base.cpp @@ -5,6 +5,8 @@ #include "core/hle/service/hid/controllers/controller_base.h" namespace Service::HID { + +ControllerBase::ControllerBase() = default; ControllerBase::~ControllerBase() = default; void ControllerBase::ActivateController() { diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h index fa98e2354..f0e092b1b 100644 --- a/src/core/hle/service/hid/controllers/controller_base.h +++ b/src/core/hle/service/hid/controllers/controller_base.h @@ -10,8 +10,8 @@ namespace Service::HID { class ControllerBase { public: - ControllerBase() = default; - virtual ~ControllerBase() = 0; + ControllerBase(); + virtual ~ControllerBase(); // Called when the controller is initialized virtual void OnInit() = 0; diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index 6f8ef6e3f..3d100763f 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp @@ -4,13 +4,13 @@ #include <cstring> #include "common/common_types.h" -#include "common/swap.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() {} diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h index e35675fa1..62b4f2682 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.h +++ b/src/core/hle/service/hid/controllers/debug_pad.h @@ -14,6 +14,7 @@ namespace Service::HID { class Controller_DebugPad final : public ControllerBase { public: Controller_DebugPad(); + ~Controller_DebugPad() override; // Called when the controller is initialized void OnInit() override; diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index b473b9e2b..898572277 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp @@ -4,7 +4,6 @@ #include <cstring> #include "common/common_types.h" -#include "common/swap.h" #include "core/core_timing.h" #include "core/hle/service/hid/controllers/gesture.h" @@ -12,6 +11,7 @@ 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() {} diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h index 0ced50dfd..1056ffbcd 100644 --- a/src/core/hle/service/hid/controllers/gesture.h +++ b/src/core/hle/service/hid/controllers/gesture.h @@ -13,6 +13,7 @@ namespace Service::HID { class Controller_Gesture final : public ControllerBase { public: Controller_Gesture(); + ~Controller_Gesture() override; // Called when the controller is initialized void OnInit() override; diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index 089c02ac4..ccfbce9ac 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp @@ -4,7 +4,6 @@ #include <cstring> #include "common/common_types.h" -#include "common/swap.h" #include "core/core_timing.h" #include "core/hle/service/hid/controllers/keyboard.h" @@ -12,6 +11,7 @@ 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() {} diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h index 778e14f7e..493e68fce 100644 --- a/src/core/hle/service/hid/controllers/keyboard.h +++ b/src/core/hle/service/hid/controllers/keyboard.h @@ -14,6 +14,7 @@ namespace Service::HID { class Controller_Keyboard final : public ControllerBase { public: Controller_Keyboard(); + ~Controller_Keyboard() override; // Called when the controller is initialized void OnInit() override; diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp index 78e9b5e9e..4e246a57d 100644 --- a/src/core/hle/service/hid/controllers/mouse.cpp +++ b/src/core/hle/service/hid/controllers/mouse.cpp @@ -4,7 +4,6 @@ #include <cstring> #include "common/common_types.h" -#include "common/swap.h" #include "core/core_timing.h" #include "core/hle/service/hid/controllers/mouse.h" @@ -12,6 +11,7 @@ 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() {} diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h index 05358a4f5..543b0b71f 100644 --- a/src/core/hle/service/hid/controllers/mouse.h +++ b/src/core/hle/service/hid/controllers/mouse.h @@ -13,6 +13,7 @@ namespace Service::HID { class Controller_Mouse final : public ControllerBase { public: Controller_Mouse(); + ~Controller_Mouse() override; // Called when the controller is initialized void OnInit() override; diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index d17e64b2a..b26593b4f 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#pragma once - #include <algorithm> #include <array> #include <cstring> @@ -11,7 +9,6 @@ #include "common/bit_field.h" #include "common/common_types.h" #include "common/logging/log.h" -#include "common/swap.h" #include "core/core.h" #include "core/core_timing.h" #include "core/frontend/input.h" @@ -20,6 +17,7 @@ #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; @@ -28,9 +26,18 @@ constexpr s32 HID_JOYSTICK_MAX = 0x7fff; constexpr s32 HID_JOYSTICK_MIN = -0x7fff; constexpr std::size_t NPAD_OFFSET = 0x9A00; constexpr u32 BATTERY_FULL = 2; -enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right }; + +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; @@ -315,7 +322,7 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) { std::memcpy(supported_npad_id_types.data(), data, length); } -const void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { +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()); } diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 9d07d258d..7c0f93acf 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -15,6 +15,7 @@ namespace Service::HID { class Controller_NPad final : public ControllerBase { public: Controller_NPad(); + ~Controller_NPad() override; // Called when the controller is initialized void OnInit() override; @@ -77,7 +78,7 @@ public: position1.Assign(light2); position1.Assign(light3); position1.Assign(light4); - }; + } union { u64 raw{}; BitField<0, 1, u64> position1; @@ -91,7 +92,7 @@ public: NPadType GetSupportedStyleSet() const; void SetSupportedNPadIdTypes(u8* data, std::size_t length); - const void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); + void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); std::size_t GetSupportedNPadIdTypesSize() const; void SetHoldType(NpadHoldType joy_hold_type); @@ -277,9 +278,7 @@ private: std::vector<u32> supported_npad_id_types{}; NpadHoldType hold_type{NpadHoldType::Vertical}; Kernel::SharedPtr<Kernel::Event> styleset_changed_event; - std::size_t dump_idx{}; Vibration last_processed_vibration{}; - static constexpr std::array<u32, 10> npad_id_list{0, 1, 2, 3, 4, 5, 6, 7, 32, 16}; std::array<ControllerHolder, 10> connected_controllers{}; bool can_controllers_vibrate{true}; diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp index 3a13d5991..02fcfadd9 100644 --- a/src/core/hle/service/hid/controllers/stubbed.cpp +++ b/src/core/hle/service/hid/controllers/stubbed.cpp @@ -4,13 +4,13 @@ #include <cstring> #include "common/common_types.h" -#include "common/swap.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() {} diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h index 9c1b57f83..4a21c643e 100644 --- a/src/core/hle/service/hid/controllers/stubbed.h +++ b/src/core/hle/service/hid/controllers/stubbed.h @@ -11,6 +11,7 @@ namespace Service::HID { class Controller_Stubbed final : public ControllerBase { public: Controller_Stubbed(); + ~Controller_Stubbed() override; // Called when the controller is initialized void OnInit() override; diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index e97f84ea1..43efef803 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp @@ -4,7 +4,6 @@ #include <cstring> #include "common/common_types.h" -#include "common/swap.h" #include "core/core_timing.h" #include "core/frontend/emu_window.h" #include "core/frontend/input.h" @@ -15,6 +14,7 @@ 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() {} diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index 1d97b6c2a..e5db6e6ba 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ b/src/core/hle/service/hid/controllers/touchscreen.h @@ -14,6 +14,7 @@ namespace Service::HID { class Controller_Touchscreen final : public ControllerBase { public: Controller_Touchscreen(); + ~Controller_Touchscreen() override; // Called when the controller is initialized void OnInit() override; diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp index df0b48451..cd397c70b 100644 --- a/src/core/hle/service/hid/controllers/xpad.cpp +++ b/src/core/hle/service/hid/controllers/xpad.cpp @@ -4,7 +4,6 @@ #include <cstring> #include "common/common_types.h" -#include "common/swap.h" #include "core/core_timing.h" #include "core/hle/service/hid/controllers/xpad.h" @@ -12,6 +11,7 @@ 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() {} diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h index e2007183d..ff836989f 100644 --- a/src/core/hle/service/hid/controllers/xpad.h +++ b/src/core/hle/service/hid/controllers/xpad.h @@ -7,13 +7,13 @@ #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_XPad final : public ControllerBase { public: Controller_XPad(); + ~Controller_XPad() override; // Called when the controller is initialized void OnInit() override; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 8aca0f197..beb89218a 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -177,6 +177,7 @@ public: {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"}, {21, &Hid::ActivateMouse, "ActivateMouse"}, {31, &Hid::ActivateKeyboard, "ActivateKeyboard"}, + {32, nullptr, "SendKeyboardLockKeyEvent"}, {40, nullptr, "AcquireXpadIdEventHandle"}, {41, nullptr, "ReleaseXpadIdEventHandle"}, {51, &Hid::ActivateXpad, "ActivateXpad"}, @@ -207,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"}, @@ -252,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"}, @@ -267,12 +270,24 @@ 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"}, }; @@ -620,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"}, @@ -630,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"}, @@ -643,6 +670,8 @@ public: {322, nullptr, "UnsetAutoPilotVirtualPadState"}, {323, nullptr, "UnsetAllAutoPilotVirtualPadState"}, {350, nullptr, "AddRegisteredDevice"}, + {400, nullptr, "DisableExternalMcuOnNxDevice"}, + {401, nullptr, "DisableRailDeviceFiltering"}, }; // clang-format on @@ -678,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"}, @@ -703,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"}, @@ -731,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"}, @@ -750,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 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/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/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/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/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp index 597b279b9..74e44c7fe 100644 --- a/src/video_core/engines/fermi_2d.cpp +++ b/src/video_core/engines/fermi_2d.cpp @@ -47,9 +47,12 @@ void Fermi2D::HandleSurfaceCopy() { u32 dst_bytes_per_pixel = RenderTargetBytesPerPixel(regs.dst.format); if (!rasterizer.AccelerateSurfaceCopy(regs.src, regs.dst)) { - // TODO(bunnei): The below implementation currently will not get hit, as - // AccelerateSurfaceCopy tries to always copy and will always return success. This should be - // changed once we properly support flushing. + rasterizer.FlushRegion(source_cpu, src_bytes_per_pixel * regs.src.width * regs.src.height); + // We have to invalidate the destination region to evict any outdated surfaces from the + // cache. We do this before actually writing the new data because the destination address + // might contain a dirty surface that will have to be written back to memory. + rasterizer.InvalidateRegion(dest_cpu, + dst_bytes_per_pixel * regs.dst.width * regs.dst.height); if (regs.src.linear == regs.dst.linear) { // If the input layout and the output layout are the same, just perform a raw copy. diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp index 66ae6332d..585290d9f 100644 --- a/src/video_core/engines/kepler_memory.cpp +++ b/src/video_core/engines/kepler_memory.cpp @@ -5,10 +5,14 @@ #include "common/logging/log.h" #include "core/memory.h" #include "video_core/engines/kepler_memory.h" +#include "video_core/rasterizer_interface.h" namespace Tegra::Engines { -KeplerMemory::KeplerMemory(MemoryManager& memory_manager) : memory_manager(memory_manager) {} +KeplerMemory::KeplerMemory(VideoCore::RasterizerInterface& rasterizer, + MemoryManager& memory_manager) + : memory_manager(memory_manager), rasterizer{rasterizer} {} + KeplerMemory::~KeplerMemory() = default; void KeplerMemory::WriteReg(u32 method, u32 value) { @@ -37,6 +41,11 @@ void KeplerMemory::ProcessData(u32 data) { VAddr dest_address = *memory_manager.GpuToCpuAddress(address + state.write_offset * sizeof(u32)); + // We have to invalidate the destination region to evict any outdated surfaces from the cache. + // We do this before actually writing the new data because the destination address might contain + // a dirty surface that will have to be written back to memory. + rasterizer.InvalidateRegion(dest_address, sizeof(u32)); + Memory::Write32(dest_address, data); state.write_offset++; diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h index b0d0078cf..bf4a13cff 100644 --- a/src/video_core/engines/kepler_memory.h +++ b/src/video_core/engines/kepler_memory.h @@ -11,6 +11,10 @@ #include "common/common_types.h" #include "video_core/memory_manager.h" +namespace VideoCore { +class RasterizerInterface; +} + namespace Tegra::Engines { #define KEPLERMEMORY_REG_INDEX(field_name) \ @@ -18,7 +22,7 @@ namespace Tegra::Engines { class KeplerMemory final { public: - KeplerMemory(MemoryManager& memory_manager); + KeplerMemory(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager); ~KeplerMemory(); /// Write the value to the register identified by method. @@ -72,6 +76,7 @@ public: private: MemoryManager& memory_manager; + VideoCore::RasterizerInterface& rasterizer; void ProcessData(u32 data); }; diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index c8d1b6478..c8af1c6b6 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -448,7 +448,10 @@ public: BitField<8, 3, u32> block_depth; BitField<12, 1, InvMemoryLayout> type; } memory_layout; - u32 array_mode; + union { + BitField<0, 16, u32> array_mode; + BitField<16, 1, u32> volume; + }; u32 layer_stride; u32 base_layer; INSERT_PADDING_WORDS(7); diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index bf2a21bb6..103cd110e 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp @@ -4,12 +4,14 @@ #include "core/memory.h" #include "video_core/engines/maxwell_dma.h" +#include "video_core/rasterizer_interface.h" #include "video_core/textures/decoders.h" namespace Tegra { namespace Engines { -MaxwellDMA::MaxwellDMA(MemoryManager& memory_manager) : memory_manager(memory_manager) {} +MaxwellDMA::MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) + : memory_manager(memory_manager), rasterizer{rasterizer} {} void MaxwellDMA::WriteReg(u32 method, u32 value) { ASSERT_MSG(method < Regs::NUM_REGS, @@ -44,38 +46,79 @@ void MaxwellDMA::HandleCopy() { ASSERT(regs.exec.query_mode == Regs::QueryMode::None); ASSERT(regs.exec.query_intr == Regs::QueryIntr::None); ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2); - ASSERT(regs.src_params.pos_x == 0); - ASSERT(regs.src_params.pos_y == 0); ASSERT(regs.dst_params.pos_x == 0); ASSERT(regs.dst_params.pos_y == 0); - if (regs.exec.is_dst_linear == regs.exec.is_src_linear) { - std::size_t copy_size = regs.x_count; + if (!regs.exec.is_dst_linear && !regs.exec.is_src_linear) { + // If both the source and the destination are in block layout, assert. + UNREACHABLE_MSG("Tiled->Tiled DMA transfers are not yet implemented"); + return; + } + if (regs.exec.is_dst_linear && regs.exec.is_src_linear) { // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D - // buffer of length `x_count`, otherwise we copy a 2D buffer of size (x_count, y_count). - if (regs.exec.enable_2d) { - copy_size = copy_size * regs.y_count; + // buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count, + // y_count). + if (!regs.exec.enable_2d) { + Memory::CopyBlock(dest_cpu, source_cpu, regs.x_count); + return; } - Memory::CopyBlock(dest_cpu, source_cpu, copy_size); + // If both the source and the destination are in linear layout, perform a line-by-line + // copy. We're going to take a subrect of size (x_count, y_count) from the source + // rectangle. There is no need to manually flush/invalidate the regions because + // CopyBlock does that for us. + for (u32 line = 0; line < regs.y_count; ++line) { + const VAddr source_line = source_cpu + line * regs.src_pitch; + const VAddr dest_line = dest_cpu + line * regs.dst_pitch; + Memory::CopyBlock(dest_line, source_line, regs.x_count); + } return; } ASSERT(regs.exec.enable_2d == 1); + + std::size_t copy_size = regs.x_count * regs.y_count; + + const auto FlushAndInvalidate = [&](u32 src_size, u32 dst_size) { + // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated + // copying. + rasterizer.FlushRegion(source_cpu, src_size); + + // We have to invalidate the destination region to evict any outdated surfaces from the + // cache. We do this before actually writing the new data because the destination address + // might contain a dirty surface that will have to be written back to memory. + rasterizer.InvalidateRegion(dest_cpu, dst_size); + }; + u8* src_buffer = Memory::GetPointer(source_cpu); u8* dst_buffer = Memory::GetPointer(dest_cpu); if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { + ASSERT(regs.src_params.size_z == 1); // If the input is tiled and the output is linear, deswizzle the input and copy it over. - Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y, - regs.src_params.size_z, 1, 1, src_buffer, dst_buffer, true, - regs.src_params.BlockHeight(), regs.src_params.BlockDepth()); + + u32 src_bytes_per_pixel = regs.src_pitch / regs.src_params.size_x; + + FlushAndInvalidate(regs.src_pitch * regs.src_params.size_y, + copy_size * src_bytes_per_pixel); + + Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch, + regs.src_params.size_x, src_bytes_per_pixel, source_cpu, dest_cpu, + regs.src_params.BlockHeight(), regs.src_params.pos_x, + regs.src_params.pos_y); } else { + ASSERT(regs.dst_params.size_z == 1); + ASSERT(regs.src_pitch == regs.x_count); + + u32 src_bpp = regs.src_pitch / regs.x_count; + + FlushAndInvalidate(regs.src_pitch * regs.y_count, + regs.dst_params.size_x * regs.dst_params.size_y * src_bpp); + // If the input is linear and the output is tiled, swizzle the input and copy it over. - Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y, - regs.dst_params.size_z, 1, 1, dst_buffer, src_buffer, false, - regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth()); + Texture::SwizzleSubrect(regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x, + src_bpp, dest_cpu, source_cpu, regs.dst_params.BlockHeight()); } } diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index df19e02e2..5f3704f05 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h @@ -12,11 +12,15 @@ #include "video_core/gpu.h" #include "video_core/memory_manager.h" +namespace VideoCore { +class RasterizerInterface; +} + namespace Tegra::Engines { class MaxwellDMA final { public: - explicit MaxwellDMA(MemoryManager& memory_manager); + explicit MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager); ~MaxwellDMA() = default; /// Write the value to the register identified by method. @@ -133,6 +137,8 @@ public: MemoryManager& memory_manager; private: + VideoCore::RasterizerInterface& rasterizer; + /// Performs the copy from the source buffer to the destination buffer as configured in the /// registers. void HandleCopy(); diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 9ba7e3533..83c7e5b0b 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -27,8 +27,8 @@ GPU::GPU(VideoCore::RasterizerInterface& rasterizer) { maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager); fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager); maxwell_compute = std::make_unique<Engines::MaxwellCompute>(); - maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager); - kepler_memory = std::make_unique<Engines::KeplerMemory>(*memory_manager); + maxwell_dma = std::make_unique<Engines::MaxwellDMA>(rasterizer, *memory_manager); + kepler_memory = std::make_unique<Engines::KeplerMemory>(rasterizer, *memory_manager); } GPU::~GPU() = default; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 468253033..3daccf82f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -659,6 +659,12 @@ void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, const Tegra::Engines::Fermi2D::Regs::Surface& dst) { MICROPROFILE_SCOPE(OpenGL_Blits); + + if (Settings::values.use_accurate_gpu_emulation) { + // Skip the accelerated copy and perform a slow but more accurate copy + return false; + } + res_cache.FermiCopySurface(src, dst); return true; } diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 1cb77aaf2..9c8925383 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -155,6 +155,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { params.rt.index = static_cast<u32>(index); params.rt.array_mode = config.array_mode; params.rt.layer_stride = config.layer_stride; + params.rt.volume = config.volume; params.rt.base_layer = config.base_layer; params.InitCacheParameters(config.Address()); @@ -1122,8 +1123,8 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool pres } else if (preserve_contents) { // If surface parameters changed and we care about keeping the previous data, recreate // the surface from the old one - Unregister(surface); Surface new_surface{RecreateSurface(surface, params)}; + Unregister(surface); Register(new_surface); return new_surface; } else { @@ -1220,6 +1221,9 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, CopySurface(old_surface, new_surface, copy_pbo.handle); } break; + case SurfaceParams::SurfaceTarget::Texture3D: + AccurateCopySurface(old_surface, new_surface); + break; case SurfaceParams::SurfaceTarget::TextureCubemap: { if (old_params.rt.array_mode != 1) { // TODO(bunnei): This is used by Breath of the Wild, I'm not sure how to implement this diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 7c1cb72d0..0dd0d90a3 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -132,6 +132,8 @@ struct SurfaceParams { case Tegra::Texture::TextureType::Texture2D: case Tegra::Texture::TextureType::Texture2DNoMipmap: return SurfaceTarget::Texture2D; + case Tegra::Texture::TextureType::Texture3D: + return SurfaceTarget::Texture3D; case Tegra::Texture::TextureType::TextureCubemap: return SurfaceTarget::TextureCubemap; case Tegra::Texture::TextureType::Texture1DArray: @@ -791,6 +793,7 @@ struct SurfaceParams { struct { u32 index; u32 array_mode; + u32 volume; u32 layer_stride; u32 base_layer; } rt; diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 55c33c3a9..f4340a017 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -1142,6 +1142,7 @@ private: case Tegra::Shader::TextureType::Texture2D: { return 2; } + case Tegra::Shader::TextureType::Texture3D: case Tegra::Shader::TextureType::TextureCube: { return 3; } diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 18ab723f7..f1b40e7f5 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -237,6 +237,46 @@ std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pix return unswizzled_data; } +void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, + u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, + u32 block_height) { + const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + 63) / 64}; + for (u32 line = 0; line < subrect_height; ++line) { + const u32 gob_address_y = + (line / (8 * block_height)) * 512 * block_height * image_width_in_gobs + + (line % (8 * block_height) / 8) * 512; + const auto& table = legacy_swizzle_table[line % 8]; + for (u32 x = 0; x < subrect_width; ++x) { + const u32 gob_address = gob_address_y + (x * bytes_per_pixel / 64) * 512 * block_height; + const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % 64]; + const VAddr source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel; + const VAddr dest_addr = swizzled_data + swizzled_offset; + + Memory::CopyBlock(dest_addr, source_line, bytes_per_pixel); + } + } +} + +void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width, + u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, + u32 block_height, u32 offset_x, u32 offset_y) { + for (u32 line = 0; line < subrect_height; ++line) { + const u32 y2 = line + offset_y; + const u32 gob_address_y = + (y2 / (8 * block_height)) * 512 * block_height + (y2 % (8 * block_height) / 8) * 512; + const auto& table = legacy_swizzle_table[y2 % 8]; + for (u32 x = 0; x < subrect_width; ++x) { + const u32 x2 = (x + offset_x) * bytes_per_pixel; + const u32 gob_address = gob_address_y + (x2 / 64) * 512 * block_height; + const u32 swizzled_offset = gob_address + table[x2 % 64]; + const VAddr dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel; + const VAddr source_addr = swizzled_data + swizzled_offset; + + Memory::CopyBlock(dest_line, source_addr, bytes_per_pixel); + } + } +} + std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width, u32 height) { std::vector<u8> rgba_data; diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h index aaf316947..4726f54a5 100644 --- a/src/video_core/textures/decoders.h +++ b/src/video_core/textures/decoders.h @@ -35,4 +35,13 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth); +/// Copies an untiled subrectangle into a tiled surface. +void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, + u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, + u32 block_height); +/// Copies a tiled subrectangle into a linear surface. +void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width, + u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, + u32 block_height, u32 offset_x, u32 offset_y); + } // namespace Tegra::Texture |