diff options
Diffstat (limited to 'src')
122 files changed, 2920 insertions, 1587 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 23e5d3f10..2e59894ab 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp @@ -8,7 +8,7 @@ #include "audio_core/codec.h" #include "common/assert.h" #include "common/logging/log.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/writable_event.h" #include "core/memory.h" namespace AudioCore { @@ -72,7 +72,7 @@ private: EffectInStatus info{}; }; AudioRenderer::AudioRenderer(AudioRendererParameter params, - Kernel::SharedPtr<Kernel::Event> buffer_event) + Kernel::SharedPtr<Kernel::WritableEvent> buffer_event) : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count), effects(params.effect_count) { diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index 71ba4be40..7826881bf 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h @@ -15,7 +15,7 @@ #include "core/hle/kernel/object.h" namespace Kernel { -class Event; +class WritableEvent; } namespace AudioCore { @@ -208,7 +208,8 @@ static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size class AudioRenderer { public: - AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event); + AudioRenderer(AudioRendererParameter params, + Kernel::SharedPtr<Kernel::WritableEvent> buffer_event); ~AudioRenderer(); std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params); @@ -224,7 +225,7 @@ private: class VoiceState; AudioRendererParameter worker_params; - Kernel::SharedPtr<Kernel::Event> buffer_event; + Kernel::SharedPtr<Kernel::WritableEvent> buffer_event; std::vector<VoiceState> voices; std::vector<EffectState> effects; std::unique_ptr<AudioOut> audio_out; diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 5753b871a..12f6d0114 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -13,7 +13,7 @@ #include <vector> #ifdef _WIN32 #include <share.h> // For _SH_DENYWR -#include <windows.h> // For OutputDebugStringA +#include <windows.h> // For OutputDebugStringW #else #define _SH_DENYWR 0 #endif @@ -148,7 +148,7 @@ void FileBackend::Write(const Entry& entry) { void DebuggerBackend::Write(const Entry& entry) { #ifdef _WIN32 - ::OutputDebugStringA(FormatLogMessage(entry).append(1, '\n').c_str()); + ::OutputDebugStringW(Common::UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str()); #endif } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e1f21a764..882c9ab59 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -63,6 +63,10 @@ add_library(core STATIC file_sys/sdmc_factory.h file_sys/submission_package.cpp file_sys/submission_package.h + file_sys/system_archive/ng_word.cpp + file_sys/system_archive/ng_word.h + file_sys/system_archive/system_archive.cpp + file_sys/system_archive/system_archive.h file_sys/vfs.cpp file_sys/vfs.h file_sys/vfs_concat.cpp @@ -97,8 +101,6 @@ add_library(core STATIC hle/kernel/client_session.cpp hle/kernel/client_session.h hle/kernel/errors.h - hle/kernel/event.cpp - hle/kernel/event.h hle/kernel/handle_table.cpp hle/kernel/handle_table.h hle/kernel/hle_ipc.cpp @@ -111,6 +113,8 @@ add_library(core STATIC hle/kernel/object.h hle/kernel/process.cpp hle/kernel/process.h + hle/kernel/readable_event.cpp + hle/kernel/readable_event.h hle/kernel/resource_limit.cpp hle/kernel/resource_limit.h hle/kernel/scheduler.cpp @@ -133,6 +137,8 @@ add_library(core STATIC hle/kernel/vm_manager.h hle/kernel/wait_object.cpp hle/kernel/wait_object.h + hle/kernel/writable_event.cpp + hle/kernel/writable_event.h hle/lock.cpp hle/lock.h hle/result.h diff --git a/src/core/core.cpp b/src/core/core.cpp index 795fabc65..ce7851538 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -8,6 +8,7 @@ #include <thread> #include <utility> +#include "common/file_util.h" #include "common/logging/log.h" #include "common/string_util.h" #include "core/arm/exclusive_monitor.h" @@ -40,7 +41,6 @@ namespace Core { /*static*/ System System::s_instance; -namespace { FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, const std::string& path) { // To account for split 00+01+etc files. @@ -69,11 +69,13 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName()); } + if (FileUtil::IsDirectory(path)) + return vfs->OpenFile(path + "/" + "main", FileSys::Mode::Read); + return vfs->OpenFile(path, FileSys::Mode::Read); } -} // Anonymous namespace - struct System::Impl { + Cpu& CurrentCpuCore() { return cpu_core_manager.GetCurrentCore(); } diff --git a/src/core/core.h b/src/core/core.h index be71bd437..71031dfcf 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -9,6 +9,7 @@ #include <string> #include "common/common_types.h" +#include "core/file_sys/vfs_types.h" #include "core/hle/kernel/object.h" namespace Core::Frontend { @@ -55,6 +56,9 @@ class TelemetrySession; struct PerfStatsResults; +FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, + const std::string& path); + class System { public: System(const System&) = delete; diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index 904afa039..ca12fb4ab 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -246,7 +246,6 @@ std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) { } std::vector<TicketRaw> out; - u32 magic{}; for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) { if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 && buffer[offset + 3] == 0x0) { @@ -794,7 +793,7 @@ void KeyManager::DeriveBase() { void KeyManager::DeriveETicket(PartitionDataManager& data) { // ETicket keys - const auto es = Service::FileSystem::GetUnionContents()->GetEntry( + const auto es = Service::FileSystem::GetUnionContents().GetEntry( 0x0100000000000033, FileSys::ContentRecordType::Program); if (es == nullptr) diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index e8df08724..ecdc21c87 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -56,6 +56,10 @@ PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} PatchManager::~PatchManager() = default; +u64 PatchManager::GetTitleID() const { + return title_id; +} + VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id); @@ -73,14 +77,18 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { const auto installed = Service::FileSystem::GetUnionContents(); + const auto& disabled = Settings::values.disabled_addons[title_id]; + const auto update_disabled = + std::find(disabled.begin(), disabled.end(), "Update") != disabled.end(); + // Game Updates const auto update_tid = GetUpdateTitleID(title_id); - const auto update = installed->GetEntry(update_tid, ContentRecordType::Program); + const auto update = installed.GetEntry(update_tid, ContentRecordType::Program); - if (update != nullptr && update->GetExeFS() != nullptr && + if (!update_disabled && update != nullptr && update->GetExeFS() != nullptr && update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully", - FormatTitleVersion(installed->GetEntryVersion(update_tid).value_or(0))); + FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0))); exefs = update->GetExeFS(); } @@ -95,6 +103,9 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { std::vector<VirtualDir> layers; layers.reserve(patch_dirs.size() + 1); for (const auto& subdir : patch_dirs) { + if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end()) + continue; + auto exefs_dir = subdir->GetSubdirectory("exefs"); if (exefs_dir != nullptr) layers.push_back(std::move(exefs_dir)); @@ -111,11 +122,16 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { return exefs; } -static std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs, - const std::string& build_id) { +std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, + const std::string& build_id) const { + const auto& disabled = Settings::values.disabled_addons[title_id]; + std::vector<VirtualFile> out; out.reserve(patch_dirs.size()); for (const auto& subdir : patch_dirs) { + if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end()) + continue; + auto exefs_dir = subdir->GetSubdirectory("exefs"); if (exefs_dir != nullptr) { for (const auto& file : exefs_dir->GetFiles()) { @@ -228,6 +244,7 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t return; } + const auto& disabled = Settings::values.disabled_addons[title_id]; auto patch_dirs = load_dir->GetSubdirectories(); std::sort(patch_dirs.begin(), patch_dirs.end(), [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); @@ -237,6 +254,9 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t layers.reserve(patch_dirs.size() + 1); layers_ext.reserve(patch_dirs.size() + 1); for (const auto& subdir : patch_dirs) { + if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end()) + continue; + auto romfs_dir = subdir->GetSubdirectory("romfs"); if (romfs_dir != nullptr) layers.push_back(std::move(romfs_dir)); @@ -281,16 +301,21 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content // Game Updates const auto update_tid = GetUpdateTitleID(title_id); - const auto update = installed->GetEntryRaw(update_tid, type); - if (update != nullptr) { + const auto update = installed.GetEntryRaw(update_tid, type); + + const auto& disabled = Settings::values.disabled_addons[title_id]; + const auto update_disabled = + std::find(disabled.begin(), disabled.end(), "Update") != disabled.end(); + + if (!update_disabled && update != nullptr) { const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset); if (new_nca->GetStatus() == Loader::ResultStatus::Success && new_nca->GetRomFS() != nullptr) { LOG_INFO(Loader, " RomFS: Update ({}) applied successfully", - FormatTitleVersion(installed->GetEntryVersion(update_tid).value_or(0))); + FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0))); romfs = new_nca->GetRomFS(); } - } else if (update_raw != nullptr) { + } else if (!update_disabled && update_raw != nullptr) { const auto new_nca = std::make_shared<NCA>(update_raw, romfs, ivfc_offset); if (new_nca->GetStatus() == Loader::ResultStatus::Success && new_nca->GetRomFS() != nullptr) { @@ -320,25 +345,30 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam VirtualFile update_raw) const { std::map<std::string, std::string, std::less<>> out; const auto installed = Service::FileSystem::GetUnionContents(); + const auto& disabled = Settings::values.disabled_addons[title_id]; // Game Updates const auto update_tid = GetUpdateTitleID(title_id); PatchManager update{update_tid}; auto [nacp, discard_icon_file] = update.GetControlMetadata(); + const auto update_disabled = + std::find(disabled.begin(), disabled.end(), "Update") != disabled.end(); + const auto update_label = update_disabled ? "[D] Update" : "Update"; + if (nacp != nullptr) { - out.insert_or_assign("Update", nacp->GetVersionString()); + out.insert_or_assign(update_label, nacp->GetVersionString()); } else { - if (installed->HasEntry(update_tid, ContentRecordType::Program)) { - const auto meta_ver = installed->GetEntryVersion(update_tid); + if (installed.HasEntry(update_tid, ContentRecordType::Program)) { + const auto meta_ver = installed.GetEntryVersion(update_tid); if (meta_ver.value_or(0) == 0) { - out.insert_or_assign("Update", ""); + out.insert_or_assign(update_label, ""); } else { out.insert_or_assign( - "Update", FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements)); + update_label, FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements)); } } else if (update_raw != nullptr) { - out.insert_or_assign("Update", "PACKED"); + out.insert_or_assign(update_label, "PACKED"); } } @@ -378,19 +408,20 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam if (types.empty()) continue; - out.insert_or_assign(mod->GetName(), types); + const auto mod_disabled = + std::find(disabled.begin(), disabled.end(), mod->GetName()) != disabled.end(); + out.insert_or_assign(mod_disabled ? "[D] " + mod->GetName() : mod->GetName(), types); } } // DLC - const auto dlc_entries = installed->ListEntriesFilter(TitleType::AOC, ContentRecordType::Data); + const auto dlc_entries = installed.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data); std::vector<RegisteredCacheEntry> dlc_match; dlc_match.reserve(dlc_entries.size()); std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), [this, &installed](const RegisteredCacheEntry& entry) { return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id && - installed->GetEntry(entry)->GetStatus() == - Loader::ResultStatus::Success; + installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; }); if (!dlc_match.empty()) { // Ensure sorted so DLC IDs show in order. @@ -402,7 +433,9 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam list += fmt::format("{}", dlc_match.back().title_id & 0x7FF); - out.insert_or_assign("DLC", std::move(list)); + const auto dlc_disabled = + std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end(); + out.insert_or_assign(dlc_disabled ? "[D] DLC" : "DLC", std::move(list)); } return out; @@ -411,7 +444,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { const auto installed{Service::FileSystem::GetUnionContents()}; - const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control); + const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control); if (base_control_nca == nullptr) return {}; diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index 7d168837f..b8a1652fd 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h @@ -30,6 +30,8 @@ public: explicit PatchManager(u64 title_id); ~PatchManager(); + u64 GetTitleID() const; + // Currently tracked ExeFS patches: // - Game Updates VirtualDir PatchExeFS(VirtualDir exefs) const; @@ -63,6 +65,9 @@ public: std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const; private: + std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs, + const std::string& build_id) const; + u64 title_id; }; diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 07c3af64a..128199063 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -107,42 +107,41 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) { VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, std::string_view path) const { const auto file = dir->GetFileRelative(path); - if (file != nullptr) + if (file != nullptr) { return file; + } const auto nca_dir = dir->GetDirectoryRelative(path); - if (nca_dir != nullptr) { - const auto nca_dir = dir->GetDirectoryRelative(path); - VirtualFile file = nullptr; + if (nca_dir == nullptr) { + return nullptr; + } - const auto files = nca_dir->GetFiles(); - if (files.size() == 1 && files[0]->GetName() == "00") { - file = files[0]; + const auto files = nca_dir->GetFiles(); + if (files.size() == 1 && files[0]->GetName() == "00") { + return files[0]; + } + + std::vector<VirtualFile> concat; + // Since the files are a two-digit hex number, max is FF. + for (std::size_t i = 0; i < 0x100; ++i) { + auto next = nca_dir->GetFile(fmt::format("{:02X}", i)); + if (next != nullptr) { + concat.push_back(std::move(next)); } else { - std::vector<VirtualFile> concat; - // Since the files are a two-digit hex number, max is FF. - for (std::size_t i = 0; i < 0x100; ++i) { - auto next = nca_dir->GetFile(fmt::format("{:02X}", i)); - if (next != nullptr) { - concat.push_back(std::move(next)); - } else { - next = nca_dir->GetFile(fmt::format("{:02x}", i)); - if (next != nullptr) - concat.push_back(std::move(next)); - else - break; - } + next = nca_dir->GetFile(fmt::format("{:02x}", i)); + if (next != nullptr) { + concat.push_back(std::move(next)); + } else { + break; } - - if (concat.empty()) - return nullptr; - - file = ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName()); } + } - return file; + if (concat.empty()) { + return nullptr; } - return nullptr; + + return ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName()); } VirtualFile RegisteredCache::GetFileAtID(NcaID id) const { diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index 0b645b106..6ad1e4f86 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp @@ -48,7 +48,7 @@ ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, Conte switch (storage) { case StorageId::None: - res = Service::FileSystem::GetUnionContents()->GetEntry(title_id, type); + res = Service::FileSystem::GetUnionContents().GetEntry(title_id, type); break; case StorageId::NandSystem: res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type); diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 5434f2149..bd50fedc7 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -18,7 +18,11 @@ std::string SaveDataDescriptor::DebugInfo() const { static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id); } -SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {} +SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) { + // Delete all temporary storages + // On hardware, it is expected that temporary storage be empty at first use. + dir->DeleteSubdirectoryRecursive("temp"); +} SaveDataFactory::~SaveDataFactory() = default; @@ -120,6 +124,8 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ case SaveDataType::TemporaryStorage: return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], title_id); + case SaveDataType::CacheStorage: + return fmt::format("{}save/cache/{:016X}", out, title_id); default: ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); } diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index 2a0088040..bd4919610 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h @@ -17,8 +17,10 @@ namespace FileSys { enum class SaveDataSpaceId : u8 { NandSystem = 0, NandUser = 1, - SdCard = 2, + SdCardSystem = 2, TemporaryStorage = 3, + SdCardUser = 4, + ProperSystem = 100, }; enum class SaveDataType : u8 { diff --git a/src/core/file_sys/system_archive/ng_word.cpp b/src/core/file_sys/system_archive/ng_word.cpp new file mode 100644 index 000000000..f4443784d --- /dev/null +++ b/src/core/file_sys/system_archive/ng_word.cpp @@ -0,0 +1,81 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <fmt/format.h> +#include "common/common_types.h" +#include "core/file_sys/system_archive/ng_word.h" +#include "core/file_sys/vfs_vector.h" + +namespace FileSys::SystemArchive { + +namespace NgWord1Data { + +constexpr std::size_t NUMBER_WORD_TXT_FILES = 0x10; + +// Should this archive replacement mysteriously not work on a future game, consider updating. +constexpr std::array<u8, 4> VERSION_DAT{0x0, 0x0, 0x0, 0x19}; // 5.1.0 System Version + +constexpr std::array<u8, 30> WORD_TXT{ + 0xFE, 0xFF, 0x00, 0x5E, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x79, 0x00, 0x62, 0x00, + 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0A, +}; // "^verybadword$" in UTF-16 + +} // namespace NgWord1Data + +VirtualDir NgWord1() { + std::vector<VirtualFile> files(NgWord1Data::NUMBER_WORD_TXT_FILES); + + for (std::size_t i = 0; i < files.size(); ++i) { + files[i] = std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>( + NgWord1Data::WORD_TXT, fmt::format("{}.txt", i)); + } + + files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>( + NgWord1Data::WORD_TXT, "common.txt")); + files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::VERSION_DAT.size()>>( + NgWord1Data::VERSION_DAT, "version.dat")); + + return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data"); +} + +namespace NgWord2Data { + +constexpr std::size_t NUMBER_AC_NX_FILES = 0x10; + +// Should this archive replacement mysteriously not work on a future game, consider updating. +constexpr std::array<u8, 4> VERSION_DAT{0x0, 0x0, 0x0, 0x15}; // 5.1.0 System Version + +constexpr std::array<u8, 0x2C> AC_NX_DATA{ + 0x1F, 0x8B, 0x08, 0x08, 0xD5, 0x2C, 0x09, 0x5C, 0x04, 0x00, 0x61, 0x63, 0x72, 0x61, 0x77, + 0x00, 0xED, 0xC1, 0x01, 0x0D, 0x00, 0x00, 0x00, 0xC2, 0x20, 0xFB, 0xA7, 0xB6, 0xC7, 0x07, + 0x0C, 0x00, 0x00, 0x00, 0xC8, 0x3B, 0x11, 0x00, 0x1C, 0xC7, 0x00, 0x10, 0x00, 0x00, +}; // Deserializes to no bad words + +} // namespace NgWord2Data + +VirtualDir NgWord2() { + std::vector<VirtualFile> files(NgWord2Data::NUMBER_AC_NX_FILES * 3); + + for (std::size_t i = 0; i < NgWord2Data::NUMBER_AC_NX_FILES; ++i) { + files[3 * i] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( + NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b1_nx", i)); + files[3 * i + 1] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( + NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b2_nx", i)); + files[3 * i + 2] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( + NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_not_b_nx", i)); + } + + files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( + NgWord2Data::AC_NX_DATA, "ac_common_b1_nx")); + files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( + NgWord2Data::AC_NX_DATA, "ac_common_b2_nx")); + files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( + NgWord2Data::AC_NX_DATA, "ac_common_not_b_nx")); + files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::VERSION_DAT.size()>>( + NgWord2Data::VERSION_DAT, "version.dat")); + + return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data"); +} + +} // namespace FileSys::SystemArchive diff --git a/src/core/file_sys/system_archive/ng_word.h b/src/core/file_sys/system_archive/ng_word.h new file mode 100644 index 000000000..cd81e0abb --- /dev/null +++ b/src/core/file_sys/system_archive/ng_word.h @@ -0,0 +1,14 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/file_sys/vfs_types.h" + +namespace FileSys::SystemArchive { + +VirtualDir NgWord1(); +VirtualDir NgWord2(); + +} // namespace FileSys::SystemArchive diff --git a/src/core/file_sys/system_archive/system_archive.cpp b/src/core/file_sys/system_archive/system_archive.cpp new file mode 100644 index 000000000..e3e79f40a --- /dev/null +++ b/src/core/file_sys/system_archive/system_archive.cpp @@ -0,0 +1,90 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/file_sys/romfs.h" +#include "core/file_sys/system_archive/ng_word.h" +#include "core/file_sys/system_archive/system_archive.h" + +namespace FileSys::SystemArchive { + +constexpr u64 SYSTEM_ARCHIVE_BASE_TITLE_ID = 0x0100000000000800; +constexpr std::size_t SYSTEM_ARCHIVE_COUNT = 0x28; + +using SystemArchiveSupplier = VirtualDir (*)(); + +struct SystemArchiveDescriptor { + u64 title_id; + const char* name; + SystemArchiveSupplier supplier; +}; + +constexpr std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHIVES{{ + {0x0100000000000800, "CertStore", nullptr}, + {0x0100000000000801, "ErrorMessage", nullptr}, + {0x0100000000000802, "MiiModel", nullptr}, + {0x0100000000000803, "BrowserDll", nullptr}, + {0x0100000000000804, "Help", nullptr}, + {0x0100000000000805, "SharedFont", nullptr}, + {0x0100000000000806, "NgWord", &NgWord1}, + {0x0100000000000807, "SsidList", nullptr}, + {0x0100000000000808, "Dictionary", nullptr}, + {0x0100000000000809, "SystemVersion", nullptr}, + {0x010000000000080A, "AvatarImage", nullptr}, + {0x010000000000080B, "LocalNews", nullptr}, + {0x010000000000080C, "Eula", nullptr}, + {0x010000000000080D, "UrlBlackList", nullptr}, + {0x010000000000080E, "TimeZoneBinary", nullptr}, + {0x010000000000080F, "CertStoreCruiser", nullptr}, + {0x0100000000000810, "FontNintendoExtension", nullptr}, + {0x0100000000000811, "FontStandard", nullptr}, + {0x0100000000000812, "FontKorean", nullptr}, + {0x0100000000000813, "FontChineseTraditional", nullptr}, + {0x0100000000000814, "FontChineseSimple", nullptr}, + {0x0100000000000815, "FontBfcpx", nullptr}, + {0x0100000000000816, "SystemUpdate", nullptr}, + {0x0100000000000817, "0100000000000817", nullptr}, + {0x0100000000000818, "FirmwareDebugSettings", nullptr}, + {0x0100000000000819, "BootImagePackage", nullptr}, + {0x010000000000081A, "BootImagePackageSafe", nullptr}, + {0x010000000000081B, "BootImagePackageExFat", nullptr}, + {0x010000000000081C, "BootImagePackageExFatSafe", nullptr}, + {0x010000000000081D, "FatalMessage", nullptr}, + {0x010000000000081E, "ControllerIcon", nullptr}, + {0x010000000000081F, "PlatformConfigIcosa", nullptr}, + {0x0100000000000820, "PlatformConfigCopper", nullptr}, + {0x0100000000000821, "PlatformConfigHoag", nullptr}, + {0x0100000000000822, "ControllerFirmware", nullptr}, + {0x0100000000000823, "NgWord2", &NgWord2}, + {0x0100000000000824, "PlatformConfigIcosaMariko", nullptr}, + {0x0100000000000825, "ApplicationBlackList", nullptr}, + {0x0100000000000826, "RebootlessSystemUpdateVersion", nullptr}, + {0x0100000000000827, "ContentActionTable", nullptr}, +}}; + +VirtualFile SynthesizeSystemArchive(const u64 title_id) { + if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id) + return nullptr; + + const auto& desc = SYSTEM_ARCHIVES[title_id - SYSTEM_ARCHIVE_BASE_TITLE_ID]; + + LOG_INFO(Service_FS, "Synthesizing system archive '{}' (0x{:016X}).", desc.name, desc.title_id); + + if (desc.supplier == nullptr) + return nullptr; + + const auto dir = desc.supplier(); + + if (dir == nullptr) + return nullptr; + + const auto romfs = CreateRomFS(dir); + + if (romfs == nullptr) + return nullptr; + + LOG_INFO(Service_FS, " - System archive generation successful!"); + return romfs; +} +} // namespace FileSys::SystemArchive diff --git a/src/core/file_sys/system_archive/system_archive.h b/src/core/file_sys/system_archive/system_archive.h new file mode 100644 index 000000000..724a8eb17 --- /dev/null +++ b/src/core/file_sys/system_archive/system_archive.h @@ -0,0 +1,14 @@ +// 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/file_sys/vfs_types.h" + +namespace FileSys::SystemArchive { + +VirtualFile SynthesizeSystemArchive(u64 title_id); + +} // namespace FileSys::SystemArchive diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index 7b584de7f..e33327ef0 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp @@ -384,6 +384,28 @@ bool VfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { return success; } +bool VfsDirectory::CleanSubdirectoryRecursive(std::string_view name) { + auto dir = GetSubdirectory(name); + if (dir == nullptr) { + return false; + } + + bool success = true; + for (const auto& file : dir->GetFiles()) { + if (!dir->DeleteFile(file->GetName())) { + success = false; + } + } + + for (const auto& sdir : dir->GetSubdirectories()) { + if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) { + success = false; + } + } + + return success; +} + bool VfsDirectory::Copy(std::string_view src, std::string_view dest) { const auto f1 = GetFile(src); auto f2 = CreateFile(dest); @@ -431,10 +453,34 @@ std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFile(std::string_view name) return nullptr; } +std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFileAbsolute(std::string_view path) { + return nullptr; +} + +std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFileRelative(std::string_view path) { + return nullptr; +} + +std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateDirectoryAbsolute(std::string_view path) { + return nullptr; +} + +std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateDirectoryRelative(std::string_view path) { + return nullptr; +} + bool ReadOnlyVfsDirectory::DeleteSubdirectory(std::string_view name) { return false; } +bool ReadOnlyVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { + return false; +} + +bool ReadOnlyVfsDirectory::CleanSubdirectoryRecursive(std::string_view name) { + return false; +} + bool ReadOnlyVfsDirectory::DeleteFile(std::string_view name) { return false; } diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h index 002f99d4e..e5641b255 100644 --- a/src/core/file_sys/vfs.h +++ b/src/core/file_sys/vfs.h @@ -245,12 +245,18 @@ public: // any failure. virtual std::shared_ptr<VfsDirectory> CreateDirectoryAbsolute(std::string_view path); - // Deletes the subdirectory with name and returns true on success. + // Deletes the subdirectory with the given name and returns true on success. virtual bool DeleteSubdirectory(std::string_view name) = 0; - // Deletes all subdirectories and files of subdirectory with name recirsively and then deletes - // the subdirectory. Returns true on success. + + // Deletes all subdirectories and files within the provided directory and then deletes + // the directory itself. Returns true on success. virtual bool DeleteSubdirectoryRecursive(std::string_view name); - // Returnes whether or not the file with name name was deleted successfully. + + // Deletes all subdirectories and files within the provided directory. + // Unlike DeleteSubdirectoryRecursive, this does not delete the provided directory. + virtual bool CleanSubdirectoryRecursive(std::string_view name); + + // Returns whether or not the file with name name was deleted successfully. virtual bool DeleteFile(std::string_view name) = 0; // Returns whether or not this directory was renamed to name. @@ -276,7 +282,13 @@ public: bool IsReadable() const override; std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override; std::shared_ptr<VfsFile> CreateFile(std::string_view name) override; + std::shared_ptr<VfsFile> CreateFileAbsolute(std::string_view path) override; + std::shared_ptr<VfsFile> CreateFileRelative(std::string_view path) override; + std::shared_ptr<VfsDirectory> CreateDirectoryAbsolute(std::string_view path) override; + std::shared_ptr<VfsDirectory> CreateDirectoryRelative(std::string_view path) override; bool DeleteSubdirectory(std::string_view name) override; + bool DeleteSubdirectoryRecursive(std::string_view name) override; + bool CleanSubdirectoryRecursive(std::string_view name) override; bool DeleteFile(std::string_view name) override; bool Rename(std::string_view name) override; }; diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp index 808f31e81..515626658 100644 --- a/src/core/file_sys/vfs_vector.cpp +++ b/src/core/file_sys/vfs_vector.cpp @@ -3,7 +3,6 @@ // Refer to the license.txt file included. #include <algorithm> -#include <cstring> #include <utility> #include "core/file_sys/vfs_vector.h" diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h index 3e3f790c3..ac36cb2ee 100644 --- a/src/core/file_sys/vfs_vector.h +++ b/src/core/file_sys/vfs_vector.h @@ -4,10 +4,63 @@ #pragma once +#include <cstring> #include "core/file_sys/vfs.h" namespace FileSys { +// An implementation of VfsFile that is backed by a statically-sized array +template <std::size_t size> +class ArrayVfsFile : public VfsFile { +public: + ArrayVfsFile(std::array<u8, size> data, std::string name = "", VirtualDir parent = nullptr) + : data(data), name(std::move(name)), parent(std::move(parent)) {} + + std::string GetName() const override { + return name; + } + + std::size_t GetSize() const override { + return size; + } + + bool Resize(std::size_t new_size) override { + return false; + } + + std::shared_ptr<VfsDirectory> GetContainingDirectory() const override { + return parent; + } + + bool IsWritable() const override { + return false; + } + + bool IsReadable() const override { + return true; + } + + std::size_t Read(u8* data_, std::size_t length, std::size_t offset) const override { + const auto read = std::min(length, size - offset); + std::memcpy(data_, data.data() + offset, read); + return read; + } + + std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override { + return 0; + } + + bool Rename(std::string_view name) override { + this->name = name; + return true; + } + +private: + std::array<u8, size> data; + std::string name; + VirtualDir parent; +}; + // An implementation of VfsFile that is backed by a vector optionally supplied upon construction class VectorVfsFile : public VfsFile { public: diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp index 1bf79b692..c8acde5b1 100644 --- a/src/core/hle/kernel/handle_table.cpp +++ b/src/core/hle/kernel/handle_table.cpp @@ -42,9 +42,10 @@ ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { u16 generation = next_generation++; // Overflow count so it fits in the 15 bits dedicated to the generation in the handle. - // CTR-OS doesn't use generation 0, so skip straight to 1. - if (next_generation >= (1 << 15)) + // Horizon OS uses zero to represent an invalid handle, so skip to 1. + if (next_generation >= (1 << 15)) { next_generation = 1; + } generations[slot] = generation; objects[slot] = std::move(obj); diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h index e3f3e3fb8..6b7927fd8 100644 --- a/src/core/hle/kernel/handle_table.h +++ b/src/core/hle/kernel/handle_table.h @@ -13,6 +13,7 @@ namespace Kernel { enum KernelHandle : Handle { + InvalidHandle = 0, CurrentThread = 0xFFFF8000, CurrentProcess = 0xFFFF8001, }; diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 68d5376cb..61ce7d7e4 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -15,13 +15,14 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/process.h" +#include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/server_session.h" +#include "core/hle/kernel/writable_event.h" #include "core/memory.h" namespace Kernel { @@ -36,11 +37,9 @@ void SessionRequestHandler::ClientDisconnected(const SharedPtr<ServerSession>& s boost::range::remove_erase(connected_sessions, server_session); } -SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread, - const std::string& reason, u64 timeout, - WakeupCallback&& callback, - Kernel::SharedPtr<Kernel::Event> event) { - +SharedPtr<WritableEvent> HLERequestContext::SleepClientThread( + SharedPtr<Thread> thread, const std::string& reason, u64 timeout, WakeupCallback&& callback, + SharedPtr<WritableEvent> writable_event) { // Put the client thread to sleep until the wait event is signaled or the timeout expires. thread->SetWakeupCallback([context = *this, callback]( ThreadWakeupReason reason, SharedPtr<Thread> thread, @@ -51,23 +50,25 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread, return true; }); - if (!event) { + auto& kernel = Core::System::GetInstance().Kernel(); + if (!writable_event) { // Create event if not provided - auto& kernel = Core::System::GetInstance().Kernel(); - event = - Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "HLE Pause Event: " + reason); + const auto pair = WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, + "HLE Pause Event: " + reason); + writable_event = pair.writable; } - event->Clear(); + const auto readable_event{writable_event->GetReadableEvent()}; + writable_event->Clear(); thread->SetStatus(ThreadStatus::WaitHLEEvent); - thread->SetWaitObjects({event}); - event->AddWaitingThread(thread); + thread->SetWaitObjects({readable_event}); + readable_event->AddWaitingThread(thread); if (timeout > 0) { thread->WakeAfterDelay(timeout); } - return event; + return writable_event; } HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session) diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index a38e34b74..e5c0610cd 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -24,10 +24,11 @@ class ServiceFrameworkBase; namespace Kernel { class Domain; -class Event; class HandleTable; class HLERequestContext; class Process; +class ReadableEvent; +class WritableEvent; /** * Interface implemented by HLE Session handlers. @@ -119,12 +120,13 @@ public: * @param callback Callback to be invoked when the thread is resumed. This callback must write * the entire command response once again, regardless of the state of it before this function * was called. - * @param event Event to use to wake up the thread. If unspecified, an event will be created. + * @param writable_event Event to use to wake up the thread. If unspecified, an event will be + * created. * @returns Event that when signaled will resume the thread and call the callback function. */ - SharedPtr<Event> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason, - u64 timeout, WakeupCallback&& callback, - Kernel::SharedPtr<Kernel::Event> event = nullptr); + SharedPtr<WritableEvent> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason, + u64 timeout, WakeupCallback&& callback, + SharedPtr<WritableEvent> writable_event = nullptr); /// Populates this context with data from the requesting process/thread. ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table, diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp index d87a62bb9..0ea851a74 100644 --- a/src/core/hle/kernel/object.cpp +++ b/src/core/hle/kernel/object.cpp @@ -13,16 +13,17 @@ Object::~Object() = default; bool Object::IsWaitable() const { switch (GetHandleType()) { - case HandleType::Event: + case HandleType::ReadableEvent: case HandleType::Thread: + case HandleType::Process: case HandleType::Timer: case HandleType::ServerPort: case HandleType::ServerSession: return true; case HandleType::Unknown: + case HandleType::WritableEvent: case HandleType::SharedMemory: - case HandleType::Process: case HandleType::AddressArbiter: case HandleType::ResourceLimit: case HandleType::ClientPort: diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h index c9f4d0bb3..f1606a204 100644 --- a/src/core/hle/kernel/object.h +++ b/src/core/hle/kernel/object.h @@ -19,7 +19,8 @@ using Handle = u32; enum class HandleType : u32 { Unknown, - Event, + WritableEvent, + ReadableEvent, SharedMemory, Thread, Process, @@ -33,9 +34,9 @@ enum class HandleType : u32 { }; enum class ResetType { - OneShot, - Sticky, - Pulse, + OneShot, ///< Reset automatically on object acquisition + Sticky, ///< Never reset automatically + Pulse, ///< Reset automatically on wakeup }; class Object : NonCopyable { diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 7ca538401..c817fb449 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -9,6 +9,7 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/file_sys/program_metadata.h" +#include "core/hle/kernel/errors.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" @@ -44,8 +45,28 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) { return process; } +SharedPtr<ResourceLimit> Process::GetResourceLimit() const { + return resource_limit; +} + +ResultCode Process::ClearSignalState() { + if (status == ProcessStatus::Exited) { + LOG_ERROR(Kernel, "called on a terminated process instance."); + return ERR_INVALID_STATE; + } + + if (!is_signaled) { + LOG_ERROR(Kernel, "called on a process instance that isn't signaled."); + return ERR_INVALID_STATE; + } + + is_signaled = false; + return RESULT_SUCCESS; +} + void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { program_id = metadata.GetTitleID(); + ideal_processor = metadata.GetMainThreadCore(); is_64bit_process = metadata.Is64BitProgram(); vm_manager.Reset(metadata.GetAddressSpaceType()); } @@ -133,13 +154,13 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { .Unwrap(); vm_manager.LogLayout(); - status = ProcessStatus::Running; + ChangeStatus(ProcessStatus::Running); Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, *this); } void Process::PrepareForTermination() { - status = ProcessStatus::Exited; + ChangeStatus(ProcessStatus::Exiting); const auto stop_threads = [this](const std::vector<SharedPtr<Thread>>& thread_list) { for (auto& thread : thread_list) { @@ -163,6 +184,8 @@ void Process::PrepareForTermination() { stop_threads(system.Scheduler(1).GetThreadList()); stop_threads(system.Scheduler(2).GetThreadList()); stop_threads(system.Scheduler(3).GetThreadList()); + + ChangeStatus(ProcessStatus::Exited); } /** @@ -261,7 +284,25 @@ ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) { return vm_manager.UnmapRange(dst_addr, size); } -Kernel::Process::Process(KernelCore& kernel) : Object{kernel} {} +Kernel::Process::Process(KernelCore& kernel) : WaitObject{kernel} {} Kernel::Process::~Process() {} +void Process::Acquire(Thread* thread) { + ASSERT_MSG(!ShouldWait(thread), "Object unavailable!"); +} + +bool Process::ShouldWait(Thread* thread) const { + return !is_signaled; +} + +void Process::ChangeStatus(ProcessStatus new_status) { + if (status == new_status) { + return; + } + + status = new_status; + is_signaled = true; + WakeupAllWaitingThreads(); +} + } // namespace Kernel diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index ada845c7f..bcb9ac4b8 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -14,9 +14,10 @@ #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" +#include "core/hle/kernel/wait_object.h" +#include "core/hle/result.h" namespace FileSys { class ProgramMetadata; @@ -117,7 +118,7 @@ struct CodeSet final { VAddr entrypoint = 0; }; -class Process final : public Object { +class Process final : public WaitObject { public: static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4; @@ -171,14 +172,7 @@ public: } /// Gets the resource limit descriptor for this process - ResourceLimit& GetResourceLimit() { - return *resource_limit; - } - - /// Gets the resource limit descriptor for this process - const ResourceLimit& GetResourceLimit() const { - return *resource_limit; - } + SharedPtr<ResourceLimit> GetResourceLimit() const; /// Gets the default CPU ID for this process u8 GetDefaultProcessorID() const { @@ -219,6 +213,16 @@ public: return random_entropy.at(index); } + /// Clears the signaled state of the process if and only if it's signaled. + /// + /// @pre The process must not be already terminated. If this is called on a + /// terminated process, then ERR_INVALID_STATE will be returned. + /// + /// @pre The process must be in a signaled state. If this is called on a + /// process instance that is not signaled, ERR_INVALID_STATE will be + /// returned. + ResultCode ClearSignalState(); + /** * Loads process-specifics configuration info with metadata provided * by an executable. @@ -267,6 +271,17 @@ private: explicit Process(KernelCore& kernel); ~Process() override; + /// Checks if the specified thread should wait until this process is available. + bool ShouldWait(Thread* thread) const override; + + /// Acquires/locks this process for the specified thread if it's available. + void Acquire(Thread* thread) override; + + /// Changes the process status. If the status is different + /// from the current process status, then this will trigger + /// a process signal. + void ChangeStatus(ProcessStatus new_status); + /// Memory manager for this process. Kernel::VMManager vm_manager; @@ -312,6 +327,10 @@ private: /// specified by metadata provided to the process during loading. bool is_64bit_process = true; + /// Whether or not this process is signaled. This occurs + /// upon the process changing to a different state. + bool is_signaled = false; + /// Total running time for the process in ticks. u64 total_process_running_time_ticks = 0; diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/readable_event.cpp index 8967e602e..ba01f495c 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/readable_event.cpp @@ -4,46 +4,47 @@ #include <algorithm> #include "common/assert.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/errors.h" #include "core/hle/kernel/object.h" +#include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/thread.h" namespace Kernel { -Event::Event(KernelCore& kernel) : WaitObject{kernel} {} -Event::~Event() = default; +ReadableEvent::ReadableEvent(KernelCore& kernel) : WaitObject{kernel} {} +ReadableEvent::~ReadableEvent() = default; -SharedPtr<Event> Event::Create(KernelCore& kernel, ResetType reset_type, std::string name) { - SharedPtr<Event> evt(new Event(kernel)); - - evt->signaled = false; - evt->reset_type = reset_type; - evt->name = std::move(name); - - return evt; -} - -bool Event::ShouldWait(Thread* thread) const { +bool ReadableEvent::ShouldWait(Thread* thread) const { return !signaled; } -void Event::Acquire(Thread* thread) { +void ReadableEvent::Acquire(Thread* thread) { ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); if (reset_type == ResetType::OneShot) signaled = false; } -void Event::Signal() { +void ReadableEvent::Signal() { signaled = true; WakeupAllWaitingThreads(); } -void Event::Clear() { +void ReadableEvent::Clear() { signaled = false; } -void Event::WakeupAllWaitingThreads() { +ResultCode ReadableEvent::Reset() { + if (!signaled) { + return ERR_INVALID_STATE; + } + + Clear(); + + return RESULT_SUCCESS; +} + +void ReadableEvent::WakeupAllWaitingThreads() { WaitObject::WakeupAllWaitingThreads(); if (reset_type == ResetType::Pulse) diff --git a/src/core/hle/kernel/readable_event.h b/src/core/hle/kernel/readable_event.h new file mode 100644 index 000000000..80b3b0aba --- /dev/null +++ b/src/core/hle/kernel/readable_event.h @@ -0,0 +1,66 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/kernel/object.h" +#include "core/hle/kernel/wait_object.h" + +union ResultCode; + +namespace Kernel { + +class KernelCore; +class WritableEvent; + +class ReadableEvent final : public WaitObject { + friend class WritableEvent; + +public: + ~ReadableEvent() override; + + std::string GetTypeName() const override { + return "ReadableEvent"; + } + std::string GetName() const override { + return name; + } + + ResetType GetResetType() const { + return reset_type; + } + + static const HandleType HANDLE_TYPE = HandleType::ReadableEvent; + HandleType GetHandleType() const override { + return HANDLE_TYPE; + } + + bool ShouldWait(Thread* thread) const override; + void Acquire(Thread* thread) override; + + void WakeupAllWaitingThreads() override; + + /// Unconditionally clears the readable event's state. + void Clear(); + + /// Clears the readable event's state if and only if it + /// has already been signaled. + /// + /// @pre The event must be in a signaled state. If this event + /// is in an unsignaled state and this function is called, + /// then ERR_INVALID_STATE will be returned. + ResultCode Reset(); + +private: + explicit ReadableEvent(KernelCore& kernel); + + void Signal(); + + ResetType reset_type; + bool signaled; + + std::string name; ///< Name of event (optional) +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 0494581f5..d1ca60125 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -39,15 +39,15 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, SharedPtr<Proce shared_memory->backing_block.get()); } } else { - auto& vm_manager = shared_memory->owner_process->VMManager(); + const auto& vm_manager = shared_memory->owner_process->VMManager(); // The memory is already available and mapped in the owner process. - auto vma = vm_manager.FindVMA(address); - ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address"); + const auto vma = vm_manager.FindVMA(address); + ASSERT_MSG(vm_manager.IsValidHandle(vma), "Invalid memory address"); ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address"); // The returned VMA might be a bigger one encompassing the desired address. - auto vma_offset = address - vma->first; + const auto vma_offset = address - vma->first; ASSERT_MSG(vma_offset + size <= vma->second.size, "Shared memory exceeds bounds of mapped block"); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 3339777c1..f43c7201c 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -20,21 +20,21 @@ #include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" -#include "core/hle/kernel/event.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/process.h" +#include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/svc.h" #include "core/hle/kernel/svc_wrap.h" #include "core/hle/kernel/thread.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/lock.h" #include "core/hle/result.h" #include "core/hle/service/service.h" -#include "core/settings.h" namespace Kernel { namespace { @@ -239,7 +239,7 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) { } const VMManager::VMAHandle iter = vm_manager.FindVMA(addr); - if (iter == vm_manager.vma_map.end()) { + if (!vm_manager.IsValidHandle(iter)) { LOG_ERROR(Kernel_SVC, "Unable to find VMA for address=0x{:016X}", addr); return ERR_INVALID_ADDRESS_STATE; } @@ -662,7 +662,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) TotalMemoryUsage = 6, TotalHeapUsage = 7, IsCurrentProcessBeingDebugged = 8, - ResourceHandleLimit = 9, + RegisterResourceLimit = 9, IdleTickCount = 10, RandomEntropy = 11, PerformanceCounter = 0xF0000002, @@ -682,37 +682,137 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) ThreadTickCount = 0xF0000002, }; - const auto* current_process = Core::CurrentProcess(); - const auto& vm_manager = current_process->VMManager(); + const auto info_id_type = static_cast<GetInfoType>(info_id); - switch (static_cast<GetInfoType>(info_id)) { + switch (info_id_type) { case GetInfoType::AllowedCpuIdBitmask: - *result = current_process->GetAllowedProcessorMask(); - break; case GetInfoType::AllowedThreadPrioBitmask: - *result = current_process->GetAllowedThreadPriorityMask(); - break; case GetInfoType::MapRegionBaseAddr: - *result = vm_manager.GetMapRegionBaseAddress(); - break; case GetInfoType::MapRegionSize: - *result = vm_manager.GetMapRegionSize(); - break; case GetInfoType::HeapRegionBaseAddr: - *result = vm_manager.GetHeapRegionBaseAddress(); - break; case GetInfoType::HeapRegionSize: - *result = vm_manager.GetHeapRegionSize(); - break; + case GetInfoType::ASLRRegionBaseAddr: + case GetInfoType::ASLRRegionSize: + case GetInfoType::NewMapRegionBaseAddr: + case GetInfoType::NewMapRegionSize: case GetInfoType::TotalMemoryUsage: - *result = vm_manager.GetTotalMemoryUsage(); - break; case GetInfoType::TotalHeapUsage: - *result = vm_manager.GetTotalHeapUsage(); - break; + case GetInfoType::IsVirtualAddressMemoryEnabled: + case GetInfoType::PersonalMmHeapUsage: + case GetInfoType::TitleId: + case GetInfoType::UserExceptionContextAddr: { + if (info_sub_id != 0) { + return ERR_INVALID_ENUM_VALUE; + } + + const auto& current_process_handle_table = Core::CurrentProcess()->GetHandleTable(); + const auto process = current_process_handle_table.Get<Process>(static_cast<Handle>(handle)); + if (!process) { + return ERR_INVALID_HANDLE; + } + + switch (info_id_type) { + case GetInfoType::AllowedCpuIdBitmask: + *result = process->GetAllowedProcessorMask(); + return RESULT_SUCCESS; + + case GetInfoType::AllowedThreadPrioBitmask: + *result = process->GetAllowedThreadPriorityMask(); + return RESULT_SUCCESS; + + case GetInfoType::MapRegionBaseAddr: + *result = process->VMManager().GetMapRegionBaseAddress(); + return RESULT_SUCCESS; + + case GetInfoType::MapRegionSize: + *result = process->VMManager().GetMapRegionSize(); + return RESULT_SUCCESS; + + case GetInfoType::HeapRegionBaseAddr: + *result = process->VMManager().GetHeapRegionBaseAddress(); + return RESULT_SUCCESS; + + case GetInfoType::HeapRegionSize: + *result = process->VMManager().GetHeapRegionSize(); + return RESULT_SUCCESS; + + case GetInfoType::ASLRRegionBaseAddr: + *result = process->VMManager().GetASLRRegionBaseAddress(); + return RESULT_SUCCESS; + + case GetInfoType::ASLRRegionSize: + *result = process->VMManager().GetASLRRegionSize(); + return RESULT_SUCCESS; + + case GetInfoType::NewMapRegionBaseAddr: + *result = process->VMManager().GetNewMapRegionBaseAddress(); + return RESULT_SUCCESS; + + case GetInfoType::NewMapRegionSize: + *result = process->VMManager().GetNewMapRegionSize(); + return RESULT_SUCCESS; + + case GetInfoType::TotalMemoryUsage: + *result = process->VMManager().GetTotalMemoryUsage(); + return RESULT_SUCCESS; + + case GetInfoType::TotalHeapUsage: + *result = process->VMManager().GetTotalHeapUsage(); + return RESULT_SUCCESS; + + case GetInfoType::IsVirtualAddressMemoryEnabled: + *result = process->IsVirtualMemoryEnabled(); + return RESULT_SUCCESS; + + case GetInfoType::TitleId: + *result = process->GetTitleID(); + return RESULT_SUCCESS; + + case GetInfoType::UserExceptionContextAddr: + LOG_WARNING(Kernel_SVC, + "(STUBBED) Attempted to query user exception context address, returned 0"); + *result = 0; + return RESULT_SUCCESS; + + default: + break; + } + + LOG_WARNING(Kernel_SVC, "(STUBBED) Unimplemented svcGetInfo id=0x{:016X}", info_id); + return ERR_INVALID_ENUM_VALUE; + } + case GetInfoType::IsCurrentProcessBeingDebugged: *result = 0; - break; + return RESULT_SUCCESS; + + case GetInfoType::RegisterResourceLimit: { + if (handle != 0) { + return ERR_INVALID_HANDLE; + } + + if (info_sub_id != 0) { + return ERR_INVALID_COMBINATION; + } + + Process* const current_process = Core::CurrentProcess(); + HandleTable& handle_table = current_process->GetHandleTable(); + const auto resource_limit = current_process->GetResourceLimit(); + if (!resource_limit) { + *result = KernelHandle::InvalidHandle; + // Yes, the kernel considers this a successful operation. + return RESULT_SUCCESS; + } + + const auto table_result = handle_table.Create(resource_limit); + if (table_result.Failed()) { + return table_result.Code(); + } + + *result = *table_result; + return RESULT_SUCCESS; + } + case GetInfoType::RandomEntropy: if (handle != 0) { LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}", @@ -726,37 +826,15 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) return ERR_INVALID_COMBINATION; } - *result = current_process->GetRandomEntropy(info_sub_id); + *result = Core::CurrentProcess()->GetRandomEntropy(info_sub_id); return RESULT_SUCCESS; - break; - case GetInfoType::ASLRRegionBaseAddr: - *result = vm_manager.GetASLRRegionBaseAddress(); - break; - case GetInfoType::ASLRRegionSize: - *result = vm_manager.GetASLRRegionSize(); - break; - case GetInfoType::NewMapRegionBaseAddr: - *result = vm_manager.GetNewMapRegionBaseAddress(); - break; - case GetInfoType::NewMapRegionSize: - *result = vm_manager.GetNewMapRegionSize(); - break; - case GetInfoType::IsVirtualAddressMemoryEnabled: - *result = current_process->IsVirtualMemoryEnabled(); - break; - case GetInfoType::TitleId: - *result = current_process->GetTitleID(); - break; + case GetInfoType::PrivilegedProcessId: LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query privileged process id bounds, returned 0"); *result = 0; - break; - case GetInfoType::UserExceptionContextAddr: - LOG_WARNING(Kernel_SVC, - "(STUBBED) Attempted to query user exception context address, returned 0"); - *result = 0; - break; + return RESULT_SUCCESS; + case GetInfoType::ThreadTickCount: { constexpr u64 num_cpus = 4; if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) { @@ -766,7 +844,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) } const auto thread = - current_process->GetHandleTable().Get<Thread>(static_cast<Handle>(handle)); + Core::CurrentProcess()->GetHandleTable().Get<Thread>(static_cast<Handle>(handle)); if (!thread) { LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", static_cast<Handle>(handle)); @@ -789,14 +867,13 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) } *result = out_ticks; - break; + return RESULT_SUCCESS; } + default: LOG_WARNING(Kernel_SVC, "(STUBBED) Unimplemented svcGetInfo id=0x{:016X}", info_id); return ERR_INVALID_ENUM_VALUE; } - - return RESULT_SUCCESS; } /// Sets the thread activity @@ -1000,19 +1077,23 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_i process_handle); return ERR_INVALID_HANDLE; } - auto vma = process->VMManager().FindVMA(addr); + + const auto& vm_manager = process->VMManager(); + const auto vma = vm_manager.FindVMA(addr); + memory_info->attributes = 0; - if (vma == process->VMManager().vma_map.end()) { - memory_info->base_address = 0; - memory_info->permission = static_cast<u32>(VMAPermission::None); - memory_info->size = 0; - memory_info->type = static_cast<u32>(MemoryState::Unmapped); - } else { + if (vm_manager.IsValidHandle(vma)) { memory_info->base_address = vma->second.base; memory_info->permission = static_cast<u32>(vma->second.permissions); memory_info->size = vma->second.size; memory_info->type = static_cast<u32>(vma->second.meminfo_state); + } else { + memory_info->base_address = 0; + memory_info->permission = static_cast<u32>(VMAPermission::None); + memory_info->size = 0; + memory_info->type = static_cast<u32>(MemoryState::Unmapped); } + return RESULT_SUCCESS; } @@ -1042,7 +1123,7 @@ static void ExitProcess() { static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, u32 priority, s32 processor_id) { LOG_TRACE(Kernel_SVC, - "called entrypoint=0x{:08X} ({}), arg=0x{:08X}, stacktop=0x{:08X}, " + "called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, " "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}", entry_point, arg, stack_top, priority, processor_id, *out_handle); @@ -1356,17 +1437,24 @@ static ResultCode CloseHandle(Handle handle) { return handle_table.Close(handle); } -/// Reset an event +/// Clears the signaled state of an event or process. static ResultCode ResetSignal(Handle handle) { LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle); const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); - auto event = handle_table.Get<Event>(handle); - ASSERT(event != nullptr); + auto event = handle_table.Get<ReadableEvent>(handle); + if (event) { + return event->Reset(); + } - event->Clear(); - return RESULT_SUCCESS; + auto process = handle_table.Get<Process>(handle); + if (process) { + return process->ClearSignalState(); + } + + LOG_ERROR(Kernel_SVC, "Invalid handle (0x{:08X})", handle); + return ERR_INVALID_HANDLE; } /// Creates a TransferMemory object @@ -1520,17 +1608,67 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss return RESULT_SUCCESS; } +static ResultCode CreateEvent(Handle* write_handle, Handle* read_handle) { + LOG_DEBUG(Kernel_SVC, "called"); + + auto& kernel = Core::System::GetInstance().Kernel(); + const auto [readable_event, writable_event] = + WritableEvent::CreateEventPair(kernel, ResetType::Sticky, "CreateEvent"); + + HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable(); + + const auto write_create_result = handle_table.Create(writable_event); + if (write_create_result.Failed()) { + return write_create_result.Code(); + } + *write_handle = *write_create_result; + + const auto read_create_result = handle_table.Create(readable_event); + if (read_create_result.Failed()) { + handle_table.Close(*write_create_result); + return read_create_result.Code(); + } + *read_handle = *read_create_result; + + LOG_DEBUG(Kernel_SVC, + "successful. Writable event handle=0x{:08X}, Readable event handle=0x{:08X}", + *write_create_result, *read_create_result); + return RESULT_SUCCESS; +} + static ResultCode ClearEvent(Handle handle) { LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); - SharedPtr<Event> evt = handle_table.Get<Event>(handle); - if (evt == nullptr) { - LOG_ERROR(Kernel_SVC, "Event handle does not exist, handle=0x{:08X}", handle); + + auto writable_event = handle_table.Get<WritableEvent>(handle); + if (writable_event) { + writable_event->Clear(); + return RESULT_SUCCESS; + } + + auto readable_event = handle_table.Get<ReadableEvent>(handle); + if (readable_event) { + readable_event->Clear(); + return RESULT_SUCCESS; + } + + LOG_ERROR(Kernel_SVC, "Event handle does not exist, handle=0x{:08X}", handle); + return ERR_INVALID_HANDLE; +} + +static ResultCode SignalEvent(Handle handle) { + LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle); + + HandleTable& handle_table = Core::CurrentProcess()->GetHandleTable(); + auto writable_event = handle_table.Get<WritableEvent>(handle); + + if (!writable_event) { + LOG_ERROR(Kernel_SVC, "Non-existent writable event handle used (0x{:08X})", handle); return ERR_INVALID_HANDLE; } - evt->Clear(); + writable_event->Signal(); return RESULT_SUCCESS; } @@ -1669,7 +1807,7 @@ static const FunctionDef SVC_Table[] = { {0x0E, SvcWrap<GetThreadCoreMask>, "GetThreadCoreMask"}, {0x0F, SvcWrap<SetThreadCoreMask>, "SetThreadCoreMask"}, {0x10, SvcWrap<GetCurrentProcessorNumber>, "GetCurrentProcessorNumber"}, - {0x11, nullptr, "SignalEvent"}, + {0x11, SvcWrap<SignalEvent>, "SignalEvent"}, {0x12, SvcWrap<ClearEvent>, "ClearEvent"}, {0x13, SvcWrap<MapSharedMemory>, "MapSharedMemory"}, {0x14, SvcWrap<UnmapSharedMemory>, "UnmapSharedMemory"}, @@ -1721,7 +1859,7 @@ static const FunctionDef SVC_Table[] = { {0x42, nullptr, "ReplyAndReceiveLight"}, {0x43, nullptr, "ReplyAndReceive"}, {0x44, nullptr, "ReplyAndReceiveWithUserBuffer"}, - {0x45, nullptr, "CreateEvent"}, + {0x45, SvcWrap<CreateEvent>, "CreateEvent"}, {0x46, nullptr, "Unknown"}, {0x47, nullptr, "Unknown"}, {0x48, nullptr, "MapPhysicalMemoryUnsafe"}, diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index fa1116624..24aef46c9 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -59,6 +59,19 @@ void SvcWrap() { FuncReturn(retval); } +template <ResultCode func(u32*, u32*)> +void SvcWrap() { + u32 param_1 = 0; + u32 param_2 = 0; + const u32 retval = func(¶m_1, ¶m_2).raw; + + auto& arm_interface = Core::CurrentArmInterface(); + arm_interface.SetReg(1, param_1); + arm_interface.SetReg(2, param_2); + + FuncReturn(retval); +} + template <ResultCode func(u32*, u64)> void SvcWrap() { u32 param_1 = 0; diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 100f8f6bf..6187993ce 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -87,6 +87,10 @@ VMManager::VMAHandle VMManager::FindVMA(VAddr target) const { } } +bool VMManager::IsValidHandle(VMAHandle handle) const { + return handle != vma_map.cend(); +} + ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block, std::size_t offset, u64 size, diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index d522404fe..a12419d1e 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -113,16 +113,10 @@ struct VirtualMemoryArea { * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/ */ class VMManager final { + using VMAMap = std::map<VAddr, VirtualMemoryArea>; + public: - /** - * A map covering the entirety of the managed address space, keyed by the `base` field of each - * VMA. It must always be modified by splitting or merging VMAs, so that the invariant - * `elem.base + elem.size == next.base` is preserved, and mergeable regions must always be - * merged when possible so that no two similar and adjacent regions exist that have not been - * merged. - */ - std::map<VAddr, VirtualMemoryArea> vma_map; - using VMAHandle = decltype(vma_map)::const_iterator; + using VMAHandle = VMAMap::const_iterator; VMManager(); ~VMManager(); @@ -133,6 +127,9 @@ public: /// Finds the VMA in which the given address is included in, or `vma_map.end()`. VMAHandle FindVMA(VAddr target) const; + /// Indicates whether or not the given handle is within the VMA map. + bool IsValidHandle(VMAHandle handle) const; + // TODO(yuriks): Should these functions actually return the handle? /** @@ -281,7 +278,7 @@ public: Memory::PageTable page_table; private: - using VMAIter = decltype(vma_map)::iterator; + using VMAIter = VMAMap::iterator; /// Converts a VMAHandle to a mutable VMAIter. VMAIter StripIterConstness(const VMAHandle& iter); @@ -328,6 +325,15 @@ private: /// Clears out the page table void ClearPageTable(); + /** + * A map covering the entirety of the managed address space, keyed by the `base` field of each + * VMA. It must always be modified by splitting or merging VMAs, so that the invariant + * `elem.base + elem.size == next.base` is preserved, and mergeable regions must always be + * merged when possible so that no two similar and adjacent regions exist that have not been + * merged. + */ + VMAMap vma_map; + u32 address_space_width = 0; VAddr address_space_base = 0; VAddr address_space_end = 0; diff --git a/src/core/hle/kernel/writable_event.cpp b/src/core/hle/kernel/writable_event.cpp new file mode 100644 index 000000000..a58ea6ec8 --- /dev/null +++ b/src/core/hle/kernel/writable_event.cpp @@ -0,0 +1,52 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include "common/assert.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/thread.h" +#include "core/hle/kernel/writable_event.h" + +namespace Kernel { + +WritableEvent::WritableEvent(KernelCore& kernel) : Object{kernel} {} +WritableEvent::~WritableEvent() = default; + +EventPair WritableEvent::CreateEventPair(KernelCore& kernel, ResetType reset_type, + std::string name) { + SharedPtr<WritableEvent> writable_event(new WritableEvent(kernel)); + SharedPtr<ReadableEvent> readable_event(new ReadableEvent(kernel)); + + writable_event->name = name + ":Writable"; + writable_event->readable = readable_event; + readable_event->name = name + ":Readable"; + readable_event->signaled = false; + readable_event->reset_type = reset_type; + + return {std::move(readable_event), std::move(writable_event)}; +} + +SharedPtr<ReadableEvent> WritableEvent::GetReadableEvent() const { + return readable; +} + +ResetType WritableEvent::GetResetType() const { + return readable->reset_type; +} + +void WritableEvent::Signal() { + readable->Signal(); +} + +void WritableEvent::Clear() { + readable->Clear(); +} + +bool WritableEvent::IsSignaled() const { + return readable->signaled; +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/writable_event.h index 27d6126b0..8fa8d68ee 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/writable_event.h @@ -11,49 +11,52 @@ namespace Kernel { class KernelCore; +class ReadableEvent; +class WritableEvent; -class Event final : public WaitObject { +struct EventPair { + SharedPtr<ReadableEvent> readable; + SharedPtr<WritableEvent> writable; +}; + +class WritableEvent final : public Object { public: + ~WritableEvent() override; + /** * Creates an event * @param kernel The kernel instance to create this event under. * @param reset_type ResetType describing how to create event * @param name Optional name of event */ - static SharedPtr<Event> Create(KernelCore& kernel, ResetType reset_type, - std::string name = "Unknown"); + static EventPair CreateEventPair(KernelCore& kernel, ResetType reset_type, + std::string name = "Unknown"); std::string GetTypeName() const override { - return "Event"; + return "WritableEvent"; } std::string GetName() const override { return name; } - static const HandleType HANDLE_TYPE = HandleType::Event; + static const HandleType HANDLE_TYPE = HandleType::WritableEvent; HandleType GetHandleType() const override { return HANDLE_TYPE; } - ResetType GetResetType() const { - return reset_type; - } - - bool ShouldWait(Thread* thread) const override; - void Acquire(Thread* thread) override; + SharedPtr<ReadableEvent> GetReadableEvent() const; - void WakeupAllWaitingThreads() override; + ResetType GetResetType() const; void Signal(); void Clear(); + bool IsSignaled() const; private: - explicit Event(KernelCore& kernel); - ~Event() override; + explicit WritableEvent(KernelCore& kernel); - ResetType reset_type; ///< Current ResetType + SharedPtr<ReadableEvent> readable; - bool signaled; ///< Whether the event has already been signaled std::string name; ///< Name of event (optional) }; diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index d595c37b0..3a7b6da84 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -9,9 +9,11 @@ #include "audio_core/audio_renderer.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" +#include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/shared_memory.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" @@ -208,8 +210,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); - launchable_event = - Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "ISelfController:LaunchableEvent"); + launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, + "ISelfController:LaunchableEvent"); } ISelfController::~ISelfController() = default; @@ -295,11 +297,11 @@ void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) { void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); - launchable_event->Signal(); + launchable_event.writable->Signal(); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(launchable_event); + rb.PushCopyObjects(launchable_event.readable); } void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx) { @@ -348,36 +350,38 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c AppletMessageQueue::AppletMessageQueue() { auto& kernel = Core::System::GetInstance().Kernel(); - on_new_message = Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, - "AMMessageQueue:OnMessageRecieved"); - on_operation_mode_changed = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, - "AMMessageQueue:OperationModeChanged"); + on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, + "AMMessageQueue:OnMessageRecieved"); + on_operation_mode_changed = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::OneShot, "AMMessageQueue:OperationModeChanged"); } AppletMessageQueue::~AppletMessageQueue() = default; -const Kernel::SharedPtr<Kernel::Event>& AppletMessageQueue::GetMesssageRecieveEvent() const { - return on_new_message; +const Kernel::SharedPtr<Kernel::ReadableEvent>& AppletMessageQueue::GetMesssageRecieveEvent() + const { + return on_new_message.readable; } -const Kernel::SharedPtr<Kernel::Event>& AppletMessageQueue::GetOperationModeChangedEvent() const { - return on_operation_mode_changed; +const Kernel::SharedPtr<Kernel::ReadableEvent>& AppletMessageQueue::GetOperationModeChangedEvent() + const { + return on_operation_mode_changed.readable; } void AppletMessageQueue::PushMessage(AppletMessage msg) { messages.push(msg); - on_new_message->Signal(); + on_new_message.writable->Signal(); } AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() { if (messages.empty()) { - on_new_message->Clear(); + on_new_message.writable->Clear(); return AppletMessage::NoMessage; } auto msg = messages.front(); messages.pop(); if (messages.empty()) { - on_new_message->Clear(); + on_new_message.writable->Clear(); } return msg; } @@ -389,7 +393,7 @@ std::size_t AppletMessageQueue::GetMessageCount() const { void AppletMessageQueue::OperationModeChanged() { PushMessage(AppletMessage::OperationModeChanged); PushMessage(AppletMessage::PerformanceModeChanged); - on_operation_mode_changed->Signal(); + on_operation_mode_changed.writable->Signal(); } ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue) @@ -426,9 +430,6 @@ ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_q // clang-format on RegisterHandlers(functions); - - auto& kernel = Core::System::GetInstance().Kernel(); - event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "ICommonStateGetter:Event"); } ICommonStateGetter::~ICommonStateGetter() = default; @@ -564,8 +565,8 @@ private: void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called"); + applet->GetBroker().SignalStateChanged(); const auto event = applet->GetBroker().GetStateChangedEvent(); - event->Signal(); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 44c1bcde5..34c45fadf 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -6,12 +6,9 @@ #include <memory> #include <queue> +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/service.h" -namespace Kernel { -class Event; -} - namespace Service { namespace NVFlinger { class NVFlinger; @@ -52,8 +49,8 @@ public: AppletMessageQueue(); ~AppletMessageQueue(); - const Kernel::SharedPtr<Kernel::Event>& GetMesssageRecieveEvent() const; - const Kernel::SharedPtr<Kernel::Event>& GetOperationModeChangedEvent() const; + const Kernel::SharedPtr<Kernel::ReadableEvent>& GetMesssageRecieveEvent() const; + const Kernel::SharedPtr<Kernel::ReadableEvent>& GetOperationModeChangedEvent() const; void PushMessage(AppletMessage msg); AppletMessage PopMessage(); std::size_t GetMessageCount() const; @@ -61,8 +58,8 @@ public: private: std::queue<AppletMessage> messages; - Kernel::SharedPtr<Kernel::Event> on_new_message; - Kernel::SharedPtr<Kernel::Event> on_operation_mode_changed; + Kernel::EventPair on_new_message; + Kernel::EventPair on_operation_mode_changed; }; class IWindowController final : public ServiceFramework<IWindowController> { @@ -122,7 +119,7 @@ private: void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); std::shared_ptr<NVFlinger::NVFlinger> nvflinger; - Kernel::SharedPtr<Kernel::Event> launchable_event; + Kernel::EventPair launchable_event; u32 idle_time_detection_extension = 0; }; @@ -151,7 +148,6 @@ private: void GetBootMode(Kernel::HLERequestContext& ctx); void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx); - Kernel::SharedPtr<Kernel::Event> event; std::shared_ptr<AppletMessageQueue> msg_queue; }; diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index becbadd06..47da35537 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -5,8 +5,9 @@ #include <cstring> #include "common/assert.h" #include "core/core.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/server_port.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/applets.h" @@ -14,11 +15,11 @@ namespace Service::AM::Applets { AppletDataBroker::AppletDataBroker() { auto& kernel = Core::System::GetInstance().Kernel(); - state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, - "ILibraryAppletAccessor:StateChangedEvent"); - pop_out_data_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, - "ILibraryAppletAccessor:PopDataOutEvent"); - pop_interactive_out_data_event = Kernel::Event::Create( + state_changed_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:StateChangedEvent"); + pop_out_data_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:PopDataOutEvent"); + pop_interactive_out_data_event = Kernel::WritableEvent::CreateEventPair( kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:PopInteractiveDataOutEvent"); } @@ -66,7 +67,7 @@ void AppletDataBroker::PushNormalDataFromGame(IStorage storage) { void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) { out_channel.push(std::make_unique<IStorage>(storage)); - pop_out_data_event->Signal(); + pop_out_data_event.writable->Signal(); } void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) { @@ -75,23 +76,23 @@ void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) { void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) { out_interactive_channel.push(std::make_unique<IStorage>(storage)); - pop_interactive_out_data_event->Signal(); + pop_interactive_out_data_event.writable->Signal(); } void AppletDataBroker::SignalStateChanged() const { - state_changed_event->Signal(); + state_changed_event.writable->Signal(); } -Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetNormalDataEvent() const { - return pop_out_data_event; +Kernel::SharedPtr<Kernel::ReadableEvent> AppletDataBroker::GetNormalDataEvent() const { + return pop_out_data_event.readable; } -Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetInteractiveDataEvent() const { - return pop_interactive_out_data_event; +Kernel::SharedPtr<Kernel::ReadableEvent> AppletDataBroker::GetInteractiveDataEvent() const { + return pop_interactive_out_data_event.readable; } -Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetStateChangedEvent() const { - return state_changed_event; +Kernel::SharedPtr<Kernel::ReadableEvent> AppletDataBroker::GetStateChangedEvent() const { + return state_changed_event.readable; } Applet::Applet() = default; diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index f65ea119c..b0a8913c3 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -8,13 +8,10 @@ #include <queue> #include "common/swap.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/writable_event.h" union ResultCode; -namespace Kernel { -class Event; -} - namespace Service::AM { class IStorage; @@ -40,9 +37,9 @@ public: void SignalStateChanged() const; - Kernel::SharedPtr<Kernel::Event> GetNormalDataEvent() const; - Kernel::SharedPtr<Kernel::Event> GetInteractiveDataEvent() const; - Kernel::SharedPtr<Kernel::Event> GetStateChangedEvent() const; + Kernel::SharedPtr<Kernel::ReadableEvent> GetNormalDataEvent() const; + Kernel::SharedPtr<Kernel::ReadableEvent> GetInteractiveDataEvent() const; + Kernel::SharedPtr<Kernel::ReadableEvent> GetStateChangedEvent() const; private: // Queues are named from applet's perspective @@ -59,13 +56,13 @@ private: // PopInteractiveDataToGame and PushInteractiveDataFromApplet std::queue<std::unique_ptr<IStorage>> out_interactive_channel; - Kernel::SharedPtr<Kernel::Event> state_changed_event; + Kernel::EventPair state_changed_event; // Signaled on PushNormalDataFromApplet - Kernel::SharedPtr<Kernel::Event> pop_out_data_event; + Kernel::EventPair pop_out_data_event; // Signaled on PushInteractiveDataFromApplet - Kernel::SharedPtr<Kernel::Event> pop_interactive_out_data_event; + Kernel::EventPair pop_interactive_out_data_event; }; class Applet { diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index bacf19de2..b506bc3dd 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -13,11 +13,14 @@ #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/kernel.h" #include "core/hle/kernel/process.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/aoc/aoc_u.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" +#include "core/settings.h" namespace Service::AOC { @@ -32,14 +35,14 @@ static std::vector<u64> AccumulateAOCTitleIDs() { std::vector<u64> add_on_content; const auto rcu = FileSystem::GetUnionContents(); const auto list = - rcu->ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); + rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); std::transform(list.begin(), list.end(), std::back_inserter(add_on_content), [](const FileSys::RegisteredCacheEntry& rce) { return rce.title_id; }); add_on_content.erase( std::remove_if( add_on_content.begin(), add_on_content.end(), [&rcu](u64 tid) { - return rcu->GetEntry(tid, FileSys::ContentRecordType::Data)->GetStatus() != + return rcu.GetEntry(tid, FileSys::ContentRecordType::Data)->GetStatus() != Loader::ResultStatus::Success; }), add_on_content.end()); @@ -61,8 +64,8 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); - aoc_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, - "GetAddOnContentListChanged:Event"); + aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, + "GetAddOnContentListChanged:Event"); } AOC_U::~AOC_U() = default; @@ -74,6 +77,13 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); + + const auto& disabled = Settings::values.disabled_addons[current]; + if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) { + rb.Push<u32>(0); + return; + } + rb.Push<u32>(static_cast<u32>( std::count_if(add_on_content.begin(), add_on_content.end(), [current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); }))); @@ -94,6 +104,10 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { out.push_back(static_cast<u32>(add_on_content[i] & 0x7FF)); } + const auto& disabled = Settings::values.disabled_addons[current]; + if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) + out = {}; + if (out.size() < offset) { IPC::ResponseBuilder rb{ctx, 2}; // TODO(DarkLordZach): Find the correct error code. @@ -144,7 +158,7 @@ void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(aoc_change_event); + rb.PushCopyObjects(aoc_change_event.readable); } void InstallInterfaces(SM::ServiceManager& service_manager) { diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h index 68d94fdaa..5effea730 100644 --- a/src/core/hle/service/aoc/aoc_u.h +++ b/src/core/hle/service/aoc/aoc_u.h @@ -6,6 +6,10 @@ #include "core/hle/service/service.h" +namespace Kernel { +class WritableEvent; +} + namespace Service::AOC { class AOC_U final : public ServiceFramework<AOC_U> { @@ -21,7 +25,7 @@ private: void GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx); std::vector<u64> add_on_content; - Kernel::SharedPtr<Kernel::Event> aoc_change_event; + Kernel::EventPair aoc_change_event; }; /// Registers all AOC services with the specified service manager. diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 2ee9bc273..dc6a6b188 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -13,8 +13,10 @@ #include "common/swap.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" #include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/audio/audout_u.h" #include "core/memory.h" @@ -46,8 +48,8 @@ class IAudioOut final : public ServiceFramework<IAudioOut> { public: IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core, std::string&& device_name, std::string&& unique_name) - : ServiceFramework("IAudioOut"), audio_core(audio_core), audio_params(audio_params), - device_name(std::move(device_name)) { + : ServiceFramework("IAudioOut"), audio_core(audio_core), + device_name(std::move(device_name)), audio_params(audio_params) { static const FunctionInfo functions[] = { {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, @@ -67,11 +69,12 @@ public: // This is the event handle used to check if the audio buffer was released auto& kernel = Core::System::GetInstance().Kernel(); - buffer_event = - Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "IAudioOutBufferReleased"); + buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, + "IAudioOutBufferReleased"); stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count, - std::move(unique_name), [=]() { buffer_event->Signal(); }); + std::move(unique_name), + [=]() { buffer_event.writable->Signal(); }); } private: @@ -121,7 +124,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(buffer_event); + rb.PushCopyObjects(buffer_event.readable); } void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { @@ -187,8 +190,8 @@ private: AudoutParams audio_params{}; - /// This is the evend handle used to check if the audio buffer was released - Kernel::SharedPtr<Kernel::Event> buffer_event; + /// This is the event handle used to check if the audio buffer was released + Kernel::EventPair buffer_event; }; void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 1c418a9bb..945259c7d 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -12,8 +12,10 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" #include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/audio/audren_u.h" namespace Service::Audio { @@ -41,14 +43,14 @@ public: RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); - system_event = - Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent"); - renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event); + system_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, + "IAudioRenderer:SystemEvent"); + renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event.writable); } private: void UpdateAudioCallback() { - system_event->Signal(); + system_event.writable->Signal(); } void GetSampleRate(Kernel::HLERequestContext& ctx) { @@ -112,7 +114,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(system_event); + rb.PushCopyObjects(system_event.readable); } void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) { @@ -135,7 +137,7 @@ private: rb.Push(rendering_time_limit_percent); } - Kernel::SharedPtr<Kernel::Event> system_event; + Kernel::EventPair system_event; std::unique_ptr<AudioCore::AudioRenderer> renderer; u32 rendering_time_limit_percent = 100; }; @@ -162,8 +164,8 @@ public: RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); - buffer_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, - "IAudioOutBufferReleasedEvent"); + buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, + "IAudioOutBufferReleasedEvent"); } private: @@ -207,11 +209,11 @@ private: void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_Audio, "(STUBBED) called"); - buffer_event->Signal(); + buffer_event.writable->Signal(); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(buffer_event); + rb.PushCopyObjects(buffer_event.readable); } void GetActiveChannelCount(Kernel::HLERequestContext& ctx) { @@ -222,7 +224,7 @@ private: rb.Push<u32>(1); } - Kernel::SharedPtr<Kernel::Event> buffer_event; + Kernel::EventPair buffer_event; }; // namespace Audio diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp index 2eadcdd05..5704ca0ab 100644 --- a/src/core/hle/service/btdrv/btdrv.cpp +++ b/src/core/hle/service/btdrv/btdrv.cpp @@ -4,8 +4,10 @@ #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" #include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/btdrv/btdrv.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" @@ -30,20 +32,22 @@ public: }; // clang-format on RegisterHandlers(functions); + + auto& kernel = Core::System::GetInstance().Kernel(); + register_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, + "BT:RegisterEvent"); } private: void RegisterEvent(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_BTM, "(STUBBED) called"); - auto& kernel = Core::System::GetInstance().Kernel(); - register_event = - Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "BT:RegisterEvent"); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(register_event); + rb.PushCopyObjects(register_event.readable); } - Kernel::SharedPtr<Kernel::Event> register_event; + + Kernel::EventPair register_event; }; class BtDrv final : public ServiceFramework<BtDrv> { diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index 463a79351..ef7398a23 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp @@ -6,8 +6,10 @@ #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" #include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/btm/btm.h" #include "core/hle/service/service.h" @@ -53,53 +55,55 @@ public: }; // clang-format on RegisterHandlers(functions); + + auto& kernel = Core::System::GetInstance().Kernel(); + scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, + "IBtmUserCore:ScanEvent"); + connection_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ConnectionEvent"); + service_discovery = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::OneShot, "IBtmUserCore:Discovery"); + config_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, + "IBtmUserCore:ConfigEvent"); } private: void GetScanEvent(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_BTM, "(STUBBED) called"); - auto& kernel = Core::System::GetInstance().Kernel(); - scan_event = - Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ScanEvent"); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(scan_event); + rb.PushCopyObjects(scan_event.readable); } + void GetConnectionEvent(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_BTM, "(STUBBED) called"); - auto& kernel = Core::System::GetInstance().Kernel(); - connection_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, - "IBtmUserCore:ConnectionEvent"); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(connection_event); + rb.PushCopyObjects(connection_event.readable); } + void GetDiscoveryEvent(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_BTM, "(STUBBED) called"); - auto& kernel = Core::System::GetInstance().Kernel(); - service_discovery = - Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:Discovery"); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(service_discovery); + rb.PushCopyObjects(service_discovery.readable); } + void GetConfigEvent(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_BTM, "(STUBBED) called"); - auto& kernel = Core::System::GetInstance().Kernel(); - config_event = - Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ConfigEvent"); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(config_event); + rb.PushCopyObjects(config_event.readable); } - Kernel::SharedPtr<Kernel::Event> scan_event; - Kernel::SharedPtr<Kernel::Event> connection_event; - Kernel::SharedPtr<Kernel::Event> service_discovery; - Kernel::SharedPtr<Kernel::Event> config_event; + + Kernel::EventPair scan_event; + Kernel::EventPair connection_event; + Kernel::EventPair service_discovery; + Kernel::EventPair config_event; }; class BTM_USR final : public ServiceFramework<BTM_USR> { diff --git a/src/core/hle/service/erpt/erpt.cpp b/src/core/hle/service/erpt/erpt.cpp index ee11cd78e..d9b32954e 100644 --- a/src/core/hle/service/erpt/erpt.cpp +++ b/src/core/hle/service/erpt/erpt.cpp @@ -17,11 +17,13 @@ public: static const FunctionInfo functions[] = { {0, nullptr, "SubmitContext"}, {1, nullptr, "CreateReport"}, - {2, nullptr, "Unknown1"}, - {3, nullptr, "Unknown2"}, - {4, nullptr, "Unknown3"}, - {5, nullptr, "Unknown4"}, - {6, nullptr, "Unknown5"}, + {2, nullptr, "SetInitialLaunchSettingsCompletionTime"}, + {3, nullptr, "ClearInitialLaunchSettingsCompletionTime"}, + {4, nullptr, "UpdatePowerOnTime"}, + {5, nullptr, "UpdateAwakeTime"}, + {6, nullptr, "SubmitMultipleCategoryContext"}, + {7, nullptr, "UpdateApplicationLaunchTime"}, + {8, nullptr, "ClearApplicationLaunchTime"}, }; // clang-format on diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 2aa77f68d..b1490e6fa 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -113,6 +113,18 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::str return RESULT_SUCCESS; } +ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const { + const std::string sanitized_path(FileUtil::SanitizePath(path)); + auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(sanitized_path)); + + if (!dir->CleanSubdirectoryRecursive(FileUtil::GetFilename(sanitized_path))) { + // TODO(DarkLordZach): Find a better error code for this + return ResultCode(-1); + } + + return RESULT_SUCCESS; +} + ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_, const std::string& dest_path_) const { std::string src_path(FileUtil::SanitizePath(src_path_)); @@ -329,20 +341,9 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() { return sdmc_factory->Open(); } -std::shared_ptr<FileSys::RegisteredCacheUnion> registered_cache_union; - -std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() { - if (registered_cache_union == nullptr) { - registered_cache_union = - std::make_shared<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{ - GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}); - } - - return registered_cache_union; -} - -void ClearUnionContents() { - registered_cache_union = nullptr; +FileSys::RegisteredCacheUnion GetUnionContents() { + return FileSys::RegisteredCacheUnion{ + {GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}}; } FileSys::RegisteredCache* GetSystemNANDContents() { @@ -395,7 +396,6 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { bis_factory = nullptr; save_data_factory = nullptr; sdmc_factory = nullptr; - ClearUnionContents(); } auto nand_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 0a6cb6635..965414be0 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -48,8 +48,7 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space); ResultVal<FileSys::VirtualDir> OpenSDMC(); -std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents(); -void ClearUnionContents(); +FileSys::RegisteredCacheUnion GetUnionContents(); FileSys::RegisteredCache* GetSystemNANDContents(); FileSys::RegisteredCache* GetUserNANDContents(); @@ -114,6 +113,18 @@ public: ResultCode DeleteDirectoryRecursively(const std::string& path) const; /** + * Cleans the specified directory. This is similar to DeleteDirectoryRecursively, + * in that it deletes all the contents of the specified directory, however, this + * function does *not* delete the directory itself. It only deletes everything + * within it. + * + * @param path Path relative to the archive. + * + * @return Result of the operation. + */ + ResultCode CleanDirectoryRecursively(const std::string& path) const; + + /** * Rename a File specified by its path * @param src_path Source path relative to the archive * @param dest_path Destination path relative to the archive diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 99d9ebc39..63fa48133 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -20,6 +20,7 @@ #include "core/file_sys/nca_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/savedata_factory.h" +#include "core/file_sys/system_archive/system_archive.h" #include "core/file_sys/vfs.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/process.h" @@ -291,7 +292,7 @@ public: {10, &IFileSystem::Commit, "Commit"}, {11, nullptr, "GetFreeSpaceSize"}, {12, nullptr, "GetTotalSpaceSize"}, - {13, nullptr, "CleanDirectoryRecursively"}, + {13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"}, {14, nullptr, "GetFileTimeStampRaw"}, {15, nullptr, "QueryEntry"}, }; @@ -361,6 +362,16 @@ public: rb.Push(backend.DeleteDirectoryRecursively(name)); } + void CleanDirectoryRecursively(Kernel::HLERequestContext& ctx) { + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + + LOG_DEBUG(Service_FS, "called. Directory: {}", name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.CleanDirectoryRecursively(name)); + } + void RenameFile(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; @@ -785,9 +796,18 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_FS, "(STUBBED) called"); + enum class LogMode : u32 { + Off, + Log, + RedirectToSdCard, + LogToSdCard = Log | RedirectToSdCard, + }; + + // Given we always want to receive logging information, + // we always specify logging as enabled. IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(5); + rb.PushEnum(LogMode::Log); } void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { @@ -821,6 +841,15 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) { auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); if (data.Failed()) { + const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id); + + if (archive != nullptr) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface(std::make_shared<IStorage>(archive)); + return; + } + // TODO(DarkLordZach): Find the right error code to use here LOG_ERROR(Service_FS, "could not open data storage with title_id={:016X}, storage_id={:02X}", title_id, diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 22e87a50a..d6829d0b8 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -12,7 +12,9 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/frontend/input.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/hid/controllers/npad.h" #include "core/settings.h" @@ -167,8 +169,8 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { void Controller_NPad::OnInit() { auto& kernel = Core::System::GetInstance().Kernel(); - styleset_changed_event = - Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged"); + styleset_changed_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged"); if (!IsControllerActivated()) { return; @@ -494,7 +496,7 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) { had_controller_update = true; } if (had_controller_update) { - styleset_changed_event->Signal(); + styleset_changed_event.writable->Signal(); } } } @@ -509,7 +511,7 @@ std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const { } void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) { - styleset_changed_event->Signal(); + styleset_changed_event.writable->Signal(); hold_type = joy_hold_type; } @@ -518,8 +520,9 @@ Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const { } 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; + const std::size_t npad_index = NPadIdToIndex(npad_id); + ASSERT(npad_index < shared_memory_entries.size()); + shared_memory_entries[npad_index].pad_assignment = assignment_mode; } void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, @@ -538,11 +541,11 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, last_processed_vibration = vibrations.back(); } -Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const { +Kernel::SharedPtr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent() const { // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should // be signalled at least once, and signaled after a new controller is connected? - styleset_changed_event->Signal(); - return styleset_changed_event; + styleset_changed_event.writable->Signal(); + return styleset_changed_event.readable; } Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index abff6544d..29851f16a 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -8,7 +8,8 @@ #include "common/bit_field.h" #include "common/common_types.h" #include "core/frontend/input.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/object.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/hid/controllers/controller_base.h" #include "core/settings.h" @@ -108,7 +109,7 @@ public: void VibrateController(const std::vector<u32>& controller_ids, const std::vector<Vibration>& vibrations); - Kernel::SharedPtr<Kernel::Event> GetStyleSetChangedEvent() const; + Kernel::SharedPtr<Kernel::ReadableEvent> GetStyleSetChangedEvent() const; Vibration GetLastVibration() const; void AddNewController(NPadControllerType controller); @@ -303,7 +304,7 @@ private: sticks; std::vector<u32> supported_npad_id_types{}; NpadHoldType hold_type{NpadHoldType::Vertical}; - Kernel::SharedPtr<Kernel::Event> styleset_changed_event; + Kernel::EventPair styleset_changed_event; Vibration last_processed_vibration{}; std::array<ControllerHolder, 10> connected_controllers{}; bool can_controllers_vibrate{true}; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 46496e9bb..2ec38c726 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -13,8 +13,9 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/shared_memory.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/hid/irs.h" #include "core/hle/service/hid/xcd.h" diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index ca119dd3a..13bcefe07 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -335,10 +335,7 @@ public: vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size, Kernel::VMAPermission::ReadWrite); - Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); - Core::System::GetInstance().ArmInterface(1).ClearInstructionCache(); - Core::System::GetInstance().ArmInterface(2).ClearInstructionCache(); - Core::System::GetInstance().ArmInterface(3).ClearInstructionCache(); + Core::System::GetInstance().InvalidateCpuInstructionCaches(); nro.insert_or_assign(*map_address, NROInfo{hash, nro_size + bss_size}); @@ -391,10 +388,7 @@ public: Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS); ASSERT(process->UnmapMemory(mapped_addr, 0, nro_size) == RESULT_SUCCESS); - Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); - Core::System::GetInstance().ArmInterface(1).ClearInstructionCache(); - Core::System::GetInstance().ArmInterface(2).ClearInstructionCache(); - Core::System::GetInstance().ArmInterface(3).ClearInstructionCache(); + Core::System::GetInstance().InvalidateCpuInstructionCaches(); nro.erase(iter); IPC::ResponseBuilder rb{ctx, 2}; @@ -414,13 +408,13 @@ private: using SHA256Hash = std::array<u8, 0x20>; struct NROHeader { - u32_le entrypoint_insn; + INSERT_PADDING_WORDS(1); u32_le mod_offset; INSERT_PADDING_WORDS(2); u32_le magic; - INSERT_PADDING_WORDS(1); + u32_le version; u32_le nro_size; - INSERT_PADDING_WORDS(1); + u32_le flags; u32_le text_offset; u32_le text_size; u32_le ro_offset; @@ -436,9 +430,10 @@ private: struct NRRHeader { u32_le magic; - INSERT_PADDING_BYTES(0x1C); + INSERT_PADDING_BYTES(12); u64_le title_id_mask; u64_le title_id_pattern; + INSERT_PADDING_BYTES(16); std::array<u8, 0x100> modulus; std::array<u8, 0x100> signature_1; std::array<u8, 0x100> signature_2; diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index ff9170c24..d5df112a0 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -7,7 +7,9 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/lock.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/nfp/nfp.h" @@ -23,8 +25,8 @@ constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP, Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) : ServiceFramework(name), module(std::move(module)) { auto& kernel = Core::System::GetInstance().Kernel(); - nfc_tag_load = - Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:NFCTagDetected"); + nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, + "IUser:NFCTagDetected"); } Module::Interface::~Interface() = default; @@ -63,10 +65,10 @@ public: RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); - deactivate_event = - Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent"); - availability_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, - "IUser:AvailabilityChangeEvent"); + deactivate_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent"); + availability_change_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::OneShot, "IUser:AvailabilityChangeEvent"); } private: @@ -164,7 +166,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(deactivate_event); + rb.PushCopyObjects(deactivate_event.readable); } void StopDetection(Kernel::HLERequestContext& ctx) { @@ -173,7 +175,7 @@ private: switch (device_state) { case DeviceState::TagFound: case DeviceState::TagNearby: - deactivate_event->Signal(); + deactivate_event.writable->Signal(); device_state = DeviceState::Initialized; break; case DeviceState::SearchingForTag: @@ -264,7 +266,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(availability_change_event); + rb.PushCopyObjects(availability_change_event.readable); } void GetRegisterInfo(Kernel::HLERequestContext& ctx) { @@ -319,8 +321,8 @@ private: const u32 npad_id{0}; // Player 1 controller State state{State::NonInitialized}; DeviceState device_state{DeviceState::Initialized}; - Kernel::SharedPtr<Kernel::Event> deactivate_event; - Kernel::SharedPtr<Kernel::Event> availability_change_event; + Kernel::EventPair deactivate_event; + Kernel::EventPair availability_change_event; const Module::Interface& nfp_interface; }; @@ -339,12 +341,14 @@ bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { } std::memcpy(&amiibo, buffer.data(), sizeof(amiibo)); - nfc_tag_load->Signal(); + nfc_tag_load.writable->Signal(); return true; } -const Kernel::SharedPtr<Kernel::Event>& Module::Interface::GetNFCEvent() const { - return nfc_tag_load; + +const Kernel::SharedPtr<Kernel::ReadableEvent>& Module::Interface::GetNFCEvent() const { + return nfc_tag_load.readable; } + const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const { return amiibo; } diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index 5c0ae8a54..a1817e991 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h @@ -6,7 +6,8 @@ #include <array> #include <vector> -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/service.h" namespace Service::NFP { @@ -33,11 +34,11 @@ public: void CreateUserInterface(Kernel::HLERequestContext& ctx); bool LoadAmiibo(const std::vector<u8>& buffer); - const Kernel::SharedPtr<Kernel::Event>& GetNFCEvent() const; + const Kernel::SharedPtr<Kernel::ReadableEvent>& GetNFCEvent() const; const AmiiboFile& GetAmiiboBuffer() const; private: - Kernel::SharedPtr<Kernel::Event> nfc_tag_load{}; + Kernel::EventPair nfc_tag_load{}; AmiiboFile amiibo{}; protected: diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index dee391201..60479bb45 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -4,7 +4,9 @@ #include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/nifm/nifm.h" #include "core/hle/service/service.h" @@ -56,8 +58,10 @@ public: RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); - event1 = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IRequest:Event1"); - event2 = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IRequest:Event2"); + event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, + "IRequest:Event1"); + event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, + "IRequest:Event2"); } private: @@ -88,7 +92,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 2}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(event1, event2); + rb.PushCopyObjects(event1.readable, event2.readable); } void Cancel(Kernel::HLERequestContext& ctx) { @@ -105,7 +109,7 @@ private: rb.Push(RESULT_SUCCESS); } - Kernel::SharedPtr<Kernel::Event> event1, event2; + Kernel::EventPair event1, event2; }; class INetworkProfile final : public ServiceFramework<INetworkProfile> { diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index 1bbccd444..0dabcd23b 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp @@ -6,7 +6,9 @@ #include <ctime> #include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/nim/nim.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" @@ -138,19 +140,18 @@ public: RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); - finished_event = - Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, - "IEnsureNetworkClockAvailabilityService:FinishEvent"); + finished_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::OneShot, + "IEnsureNetworkClockAvailabilityService:FinishEvent"); } private: - Kernel::SharedPtr<Kernel::Event> finished_event; + Kernel::EventPair finished_event; void StartTask(Kernel::HLERequestContext& ctx) { // No need to connect to the internet, just finish the task straight away. LOG_DEBUG(Service_NIM, "called"); - - finished_event->Signal(); + finished_event.writable->Signal(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -160,7 +161,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(finished_event); + rb.PushCopyObjects(finished_event.readable); } void GetResult(Kernel::HLERequestContext& ctx) { @@ -172,8 +173,7 @@ private: void Cancel(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NIM, "called"); - - finished_event->Clear(); + finished_event.writable->Clear(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp index ff76e0524..3b9ab4b14 100644 --- a/src/core/hle/service/nvdrv/interface.cpp +++ b/src/core/hle/service/nvdrv/interface.cpp @@ -6,7 +6,9 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/nvdrv/interface.h" #include "core/hle/service/nvdrv/nvdrv.h" @@ -69,7 +71,7 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(query_event); + rb.PushCopyObjects(query_event.readable); rb.Push<u32>(0); } @@ -127,7 +129,8 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name) RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); - query_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "NVDRV::query_event"); + query_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, + "NVDRV::query_event"); } NVDRV::~NVDRV() = default; diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h index 5a1e4baa7..fe311b069 100644 --- a/src/core/hle/service/nvdrv/interface.h +++ b/src/core/hle/service/nvdrv/interface.h @@ -5,10 +5,13 @@ #pragma once #include <memory> -#include "core/hle/kernel/event.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/service.h" +namespace Kernel { +class WritableEvent; +} + namespace Service::Nvidia { class NVDRV final : public ServiceFramework<NVDRV> { @@ -31,7 +34,7 @@ private: u64 pid{}; - Kernel::SharedPtr<Kernel::Event> query_event; + Kernel::EventPair query_event; }; } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index 172a1a441..fc07d9bb8 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -7,14 +7,17 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/nvflinger/buffer_queue.h" namespace Service::NVFlinger { BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) { auto& kernel = Core::System::GetInstance().Kernel(); - buffer_wait_event = - Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "BufferQueue NativeHandle"); + buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, + "BufferQueue NativeHandle"); } BufferQueue::~BufferQueue() = default; @@ -28,7 +31,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) buffer.status = Buffer::Status::Free; queue.emplace_back(buffer); - buffer_wait_event->Signal(); + buffer_wait_event.writable->Signal(); } std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) { @@ -87,7 +90,7 @@ void BufferQueue::ReleaseBuffer(u32 slot) { ASSERT(itr->status == Buffer::Status::Acquired); itr->status = Buffer::Status::Free; - buffer_wait_event->Signal(); + buffer_wait_event.writable->Signal(); } u32 BufferQueue::Query(QueryType type) { @@ -104,4 +107,12 @@ u32 BufferQueue::Query(QueryType type) { return 0; } +Kernel::SharedPtr<Kernel::WritableEvent> BufferQueue::GetWritableBufferWaitEvent() const { + return buffer_wait_event.writable; +} + +Kernel::SharedPtr<Kernel::ReadableEvent> BufferQueue::GetBufferWaitEvent() const { + return buffer_wait_event.readable; +} + } // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h index 8cff5eb71..b171f256c 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ b/src/core/hle/service/nvflinger/buffer_queue.h @@ -10,7 +10,8 @@ #include "common/common_funcs.h" #include "common/math_util.h" #include "common/swap.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/object.h" +#include "core/hle/kernel/writable_event.h" namespace CoreTiming { struct EventType; @@ -86,16 +87,16 @@ public: return id; } - Kernel::SharedPtr<Kernel::Event> GetBufferWaitEvent() const { - return buffer_wait_event; - } + Kernel::SharedPtr<Kernel::WritableEvent> GetWritableBufferWaitEvent() const; + + Kernel::SharedPtr<Kernel::ReadableEvent> GetBufferWaitEvent() const; private: u32 id; u64 layer_id; std::vector<Buffer> queue; - Kernel::SharedPtr<Kernel::Event> buffer_wait_event; + Kernel::EventPair buffer_wait_event; }; } // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 214e6d1b3..05af2d593 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -13,6 +13,9 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/core_timing_util.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvflinger/buffer_queue.h" @@ -83,9 +86,8 @@ u32 NVFlinger::GetBufferQueueId(u64 display_id, u64 layer_id) { return layer.buffer_queue->GetId(); } -Kernel::SharedPtr<Kernel::Event> NVFlinger::GetVsyncEvent(u64 display_id) { - const auto& display = GetDisplay(display_id); - return display.vsync_event; +Kernel::SharedPtr<Kernel::ReadableEvent> NVFlinger::GetVsyncEvent(u64 display_id) { + return GetDisplay(display_id).vsync_event.readable; } std::shared_ptr<BufferQueue> NVFlinger::GetBufferQueue(u32 id) const { @@ -117,7 +119,7 @@ Layer& NVFlinger::GetLayer(u64 display_id, u64 layer_id) { void NVFlinger::Compose() { for (auto& display : displays) { // Trigger vsync for this display at the end of drawing - SCOPE_EXIT({ display.vsync_event->Signal(); }); + SCOPE_EXIT({ display.vsync_event.writable->Signal(); }); // Don't do anything for displays without layers. if (display.layers.empty()) @@ -164,7 +166,8 @@ Layer::~Layer() = default; Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) { auto& kernel = Core::System::GetInstance().Kernel(); - vsync_event = Kernel::Event::Create(kernel, Kernel::ResetType::Pulse, "Display VSync Event"); + vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Pulse, + fmt::format("Display VSync Event {}", id)); } Display::~Display() = default; diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 3dc69e69b..9abba555b 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -10,12 +10,17 @@ #include <vector> #include "common/common_types.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/object.h" namespace CoreTiming { struct EventType; } +namespace Kernel { +class ReadableEvent; +class WritableEvent; +} // namespace Kernel + namespace Service::Nvidia { class Module; } @@ -40,7 +45,7 @@ struct Display { std::string name; std::vector<Layer> layers; - Kernel::SharedPtr<Kernel::Event> vsync_event; + Kernel::EventPair vsync_event; }; class NVFlinger final { @@ -61,7 +66,7 @@ public: u32 GetBufferQueueId(u64 display_id, u64 layer_id); /// Gets the vsync event for the specified display. - Kernel::SharedPtr<Kernel::Event> GetVsyncEvent(u64 display_id); + Kernel::SharedPtr<Kernel::ReadableEvent> GetVsyncEvent(u64 display_id); /// Obtains a buffer queue identified by the id. std::shared_ptr<BufferQueue> GetBufferQueue(u32 id) const; diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 1ec340466..d41df3732 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -70,10 +70,6 @@ #include "core/hle/service/vi/vi.h" #include "core/hle/service/wlan/wlan.h" -using Kernel::ClientPort; -using Kernel::ServerPort; -using Kernel::SharedPtr; - namespace Service { /** @@ -110,10 +106,8 @@ void ServiceFrameworkBase::InstallAsNamedPort() { ASSERT(port == nullptr); auto& kernel = Core::System::GetInstance().Kernel(); - SharedPtr<ServerPort> server_port; - SharedPtr<ClientPort> client_port; - std::tie(server_port, client_port) = - ServerPort::CreatePortPair(kernel, max_sessions, service_name); + auto [server_port, client_port] = + Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name); server_port->SetHleHandler(shared_from_this()); kernel.AddNamedPort(service_name, std::move(client_port)); } @@ -122,11 +116,9 @@ Kernel::SharedPtr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() { ASSERT(port == nullptr); auto& kernel = Core::System::GetInstance().Kernel(); - Kernel::SharedPtr<Kernel::ServerPort> server_port; - Kernel::SharedPtr<Kernel::ClientPort> client_port; - std::tie(server_port, client_port) = + auto [server_port, client_port] = Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name); - port = MakeResult<Kernel::SharedPtr<Kernel::ServerPort>>(std::move(server_port)).Unwrap(); + port = MakeResult(std::move(server_port)).Unwrap(); port->SetHleHandler(shared_from_this()); return client_port; } @@ -152,8 +144,7 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext } buf.push_back('}'); - LOG_ERROR(Service, "unknown / unimplemented {}", fmt::to_string(buf)); - UNIMPLEMENTED(); + UNIMPLEMENTED_MSG("Unknown / unimplemented {}", fmt::to_string(buf)); } void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 0d0f63a78..d73530086 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -54,13 +54,11 @@ ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> ServiceManager::RegisterService return ERR_ALREADY_REGISTERED; auto& kernel = Core::System::GetInstance().Kernel(); - Kernel::SharedPtr<Kernel::ServerPort> server_port; - Kernel::SharedPtr<Kernel::ClientPort> client_port; - std::tie(server_port, client_port) = + auto [server_port, client_port] = Kernel::ServerPort::CreatePortPair(kernel, max_sessions, name); registered_services.emplace(std::move(name), std::move(client_port)); - return MakeResult<Kernel::SharedPtr<Kernel::ServerPort>>(std::move(server_port)); + return MakeResult(std::move(server_port)); } ResultCode ServiceManager::UnregisterService(const std::string& name) { @@ -83,7 +81,7 @@ ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> ServiceManager::GetServicePort( return ERR_SERVICE_NOT_REGISTERED; } - return MakeResult<Kernel::SharedPtr<Kernel::ClientPort>>(it->second); + return MakeResult(it->second); } ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ServiceManager::ConnectToService( diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp index f082a63bc..58a9845fc 100644 --- a/src/core/hle/service/usb/usb.cpp +++ b/src/core/hle/service/usb/usb.cpp @@ -73,7 +73,7 @@ public: {3, nullptr, "Populate"}, {4, nullptr, "PostBufferAsync"}, {5, nullptr, "GetXferReport"}, - {6, nullptr, "Unknown2"}, + {6, nullptr, "PostBufferMultiAsync"}, {7, nullptr, "Unknown3"}, {8, nullptr, "Unknown4"}, }; diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 412d5b0c9..311b0c765 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -18,7 +18,8 @@ #include "common/swap.h" #include "core/core_timing.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvflinger/buffer_queue.h" #include "core/hle/service/nvflinger/nvflinger.h" @@ -549,7 +550,7 @@ private: IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); }, - buffer_queue->GetBufferWaitEvent()); + buffer_queue->GetWritableBufferWaitEvent()); } } else if (transaction == TransactionId::RequestBuffer) { IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()}; diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 8518dddcb..ac04d72d7 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -7,7 +7,6 @@ #include "common/common_funcs.h" #include "common/file_util.h" #include "common/logging/log.h" -#include "core/core.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" @@ -146,7 +145,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process) const VAddr load_addr = next_load_addr; const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; const auto tentative_next_load_addr = - AppLoader_NSO::LoadModule(*module_file, load_addr, should_pass_arguments, pm); + AppLoader_NSO::LoadModule(process, *module_file, load_addr, should_pass_arguments, pm); if (!tentative_next_load_addr) { return ResultStatus::ErrorLoadingNSO; } diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index d109ed2b5..1615cb5a8 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h @@ -33,7 +33,7 @@ public: */ static FileType IdentifyType(const FileSys::VirtualFile& file); - FileType GetFileType() override { + FileType GetFileType() const override { return IdentifyType(file); } diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h index 6af76441c..a2d33021c 100644 --- a/src/core/loader/elf.h +++ b/src/core/loader/elf.h @@ -22,7 +22,7 @@ public: */ static FileType IdentifyType(const FileSys::VirtualFile& file); - FileType GetFileType() override { + FileType GetFileType() const override { return IdentifyType(file); } diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 7686634bf..0838e303b 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -12,6 +12,7 @@ #include <vector> #include "common/common_types.h" +#include "core/file_sys/control_metadata.h" #include "core/file_sys/vfs.h" namespace Kernel { @@ -131,7 +132,7 @@ public: * Returns the type of this file * @return FileType corresponding to the loaded file */ - virtual FileType GetFileType() = 0; + virtual FileType GetFileType() const = 0; /** * Load the application and return the created Process instance @@ -243,6 +244,15 @@ public: return ResultStatus::ErrorNotImplemented; } + /** + * Get the developer of the application + * @param developer Reference to store the application developer into + * @return ResultStatus result of function + */ + virtual ResultStatus ReadDeveloper(std::string& developer) { + return ResultStatus::ErrorNotImplemented; + } + protected: FileSys::VirtualFile file; bool is_loaded = false; diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp index 42f4a777b..a093e3d36 100644 --- a/src/core/loader/nax.cpp +++ b/src/core/loader/nax.cpp @@ -37,7 +37,7 @@ FileType AppLoader_NAX::IdentifyType(const FileSys::VirtualFile& file) { return IdentifyTypeImpl(nax); } -FileType AppLoader_NAX::GetFileType() { +FileType AppLoader_NAX::GetFileType() const { return IdentifyTypeImpl(*nax); } diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h index b4d93bd01..0a97511b8 100644 --- a/src/core/loader/nax.h +++ b/src/core/loader/nax.h @@ -31,7 +31,7 @@ public: */ static FileType IdentifyType(const FileSys::VirtualFile& file); - FileType GetFileType() override; + FileType GetFileType() const override; ResultStatus Load(Kernel::Process& process) override; diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h index 95d9b73a1..cbbe701d2 100644 --- a/src/core/loader/nca.h +++ b/src/core/loader/nca.h @@ -29,7 +29,7 @@ public: */ static FileType IdentifyType(const FileSys::VirtualFile& file); - FileType GetFileType() override { + FileType GetFileType() const override { return IdentifyType(file); } diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index fbbd6b0de..4fad0c0dd 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -10,7 +10,6 @@ #include "common/file_util.h" #include "common/logging/log.h" #include "common/swap.h" -#include "core/core.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/romfs_factory.h" #include "core/file_sys/vfs_offset.h" @@ -129,9 +128,8 @@ static constexpr u32 PageAlignSize(u32 size) { return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; } -/*static*/ bool AppLoader_NRO::LoadNro(const std::vector<u8>& data, const std::string& name, - VAddr load_base) { - +static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data, + const std::string& name, VAddr load_base) { if (data.size() < sizeof(NroHeader)) { return {}; } @@ -189,7 +187,7 @@ static constexpr u32 PageAlignSize(u32 size) { // Load codeset for current process codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image)); - Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); + process.LoadModule(std::move(codeset), load_base); // Register module with GDBStub GDBStub::RegisterModule(name, load_base, load_base); @@ -197,8 +195,9 @@ static constexpr u32 PageAlignSize(u32 size) { return true; } -bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) { - return AppLoader_NRO::LoadNro(file.ReadAllBytes(), file.GetName(), load_base); +bool AppLoader_NRO::LoadNro(Kernel::Process& process, const FileSys::VfsFile& file, + VAddr load_base) { + return LoadNroImpl(process, file.ReadAllBytes(), file.GetName(), load_base); } ResultStatus AppLoader_NRO::Load(Kernel::Process& process) { @@ -209,7 +208,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::Process& process) { // Load NRO const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); - if (!LoadNro(*file, base_address)) { + if (!LoadNro(process, *file, base_address)) { return ResultStatus::ErrorLoadingNRO; } diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index 3e6959302..013d629c0 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h @@ -14,6 +14,10 @@ namespace FileSys { class NACP; } +namespace Kernel { +class Process; +} + namespace Loader { /// Loads an NRO file @@ -29,7 +33,7 @@ public: */ static FileType IdentifyType(const FileSys::VirtualFile& file); - FileType GetFileType() override { + FileType GetFileType() const override { return IdentifyType(file); } @@ -41,10 +45,8 @@ public: ResultStatus ReadTitle(std::string& title) override; bool IsRomFSUpdatable() const override; - static bool LoadNro(const std::vector<u8>& data, const std::string& name, VAddr load_base); - private: - bool LoadNro(const FileSys::VfsFile& file, VAddr load_base); + bool LoadNro(Kernel::Process& process, const FileSys::VfsFile& file, VAddr load_base); std::vector<u8> icon_data; std::unique_ptr<FileSys::NACP> nacp; diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index aaf006309..6ded0b707 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -9,7 +9,6 @@ #include "common/file_util.h" #include "common/logging/log.h" #include "common/swap.h" -#include "core/core.h" #include "core/file_sys/patch_manager.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/process.h" @@ -93,7 +92,8 @@ static constexpr u32 PageAlignSize(u32 size) { return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; } -std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAddr load_base, +std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, + const FileSys::VfsFile& file, VAddr load_base, bool should_pass_arguments, std::optional<FileSys::PatchManager> pm) { if (file.GetSize() < sizeof(NsoHeader)) @@ -166,7 +166,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAd // Load codeset for current process codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image)); - Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); + process.LoadModule(std::move(codeset), load_base); // Register module with GDBStub GDBStub::RegisterModule(file.GetName(), load_base, load_base); @@ -181,7 +181,7 @@ ResultStatus AppLoader_NSO::Load(Kernel::Process& process) { // Load module const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); - if (!LoadModule(*file, base_address, true)) { + if (!LoadModule(process, *file, base_address, true)) { return ResultStatus::ErrorLoadingNSO; } LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index 433306139..135b6ea5a 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h @@ -10,6 +10,10 @@ #include "core/loader/linker.h" #include "core/loader/loader.h" +namespace Kernel { +class Process; +} + namespace Loader { constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000; @@ -33,12 +37,12 @@ public: */ static FileType IdentifyType(const FileSys::VirtualFile& file); - FileType GetFileType() override { + FileType GetFileType() const override { return IdentifyType(file); } - static std::optional<VAddr> LoadModule(const FileSys::VfsFile& file, VAddr load_base, - bool should_pass_arguments, + static std::optional<VAddr> LoadModule(Kernel::Process& process, const FileSys::VfsFile& file, + VAddr load_base, bool should_pass_arguments, std::optional<FileSys::PatchManager> pm = {}); ResultStatus Load(Kernel::Process& process) override; diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index 080d89904..b4ab88ae8 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp @@ -151,4 +151,11 @@ ResultStatus AppLoader_NSP::ReadTitle(std::string& title) { title = nacp_file->GetApplicationName(); return ResultStatus::Success; } + +ResultStatus AppLoader_NSP::ReadDeveloper(std::string& developer) { + if (nacp_file == nullptr) + return ResultStatus::ErrorNoControl; + developer = nacp_file->GetDeveloperName(); + return ResultStatus::Success; +} } // namespace Loader diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h index db91cd01e..2b1e0719b 100644 --- a/src/core/loader/nsp.h +++ b/src/core/loader/nsp.h @@ -31,7 +31,7 @@ public: */ static FileType IdentifyType(const FileSys::VirtualFile& file); - FileType GetFileType() override { + FileType GetFileType() const override { return IdentifyType(file); } @@ -43,6 +43,7 @@ public: ResultStatus ReadProgramId(u64& out_program_id) override; ResultStatus ReadIcon(std::vector<u8>& buffer) override; ResultStatus ReadTitle(std::string& title) override; + ResultStatus ReadDeveloper(std::string& developer) override; private: std::unique_ptr<FileSys::NSP> nsp; diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index 461607c95..bd5a83b49 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp @@ -120,4 +120,11 @@ ResultStatus AppLoader_XCI::ReadTitle(std::string& title) { title = nacp_file->GetApplicationName(); return ResultStatus::Success; } + +ResultStatus AppLoader_XCI::ReadDeveloper(std::string& developer) { + if (nacp_file == nullptr) + return ResultStatus::ErrorNoControl; + developer = nacp_file->GetDeveloperName(); + return ResultStatus::Success; +} } // namespace Loader diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h index 46f8dfc9e..15d1b1a23 100644 --- a/src/core/loader/xci.h +++ b/src/core/loader/xci.h @@ -31,7 +31,7 @@ public: */ static FileType IdentifyType(const FileSys::VirtualFile& file); - FileType GetFileType() override { + FileType GetFileType() const override { return IdentifyType(file); } @@ -43,6 +43,7 @@ public: ResultStatus ReadProgramId(u64& out_program_id) override; ResultStatus ReadIcon(std::vector<u8>& buffer) override; ResultStatus ReadTitle(std::string& title) override; + ResultStatus ReadDeveloper(std::string& developer) override; private: std::unique_ptr<FileSys::XCI> xci; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 70abd856a..643afdee8 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -53,6 +53,14 @@ void PageTable::Resize(std::size_t address_space_width_in_bits) { pointers.resize(num_page_table_entries); attributes.resize(num_page_table_entries); + + // The default is a 39-bit address space, which causes an initial 1GB allocation size. If the + // vector size is subsequently decreased (via resize), the vector might not automatically + // actually reallocate/resize its underlying allocation, which wastes up to ~800 MB for + // 36-bit titles. Call shrink_to_fit to reduce capacity to what's actually in use. + + pointers.shrink_to_fit(); + attributes.shrink_to_fit(); } static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, PageType type) { @@ -117,14 +125,13 @@ void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPoin * using a VMA from the current process */ static u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) { - u8* direct_pointer = nullptr; + const auto& vm_manager = process.VMManager(); - auto& vm_manager = process.VMManager(); + const auto it = vm_manager.FindVMA(vaddr); + DEBUG_ASSERT(vm_manager.IsValidHandle(it)); - auto it = vm_manager.FindVMA(vaddr); - ASSERT(it != vm_manager.vma_map.end()); - - auto& vma = it->second; + u8* direct_pointer = nullptr; + const auto& vma = it->second; switch (vma.type) { case Kernel::VMAType::AllocatedMemoryBlock: direct_pointer = vma.backing_block->data() + vma.offset; diff --git a/src/core/settings.h b/src/core/settings.h index a0c5fd447..de01b05c0 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -6,8 +6,10 @@ #include <array> #include <atomic> +#include <map> #include <optional> #include <string> +#include <vector> #include "common/common_types.h" namespace Settings { @@ -411,6 +413,9 @@ struct Values { std::string web_api_url; std::string yuzu_username; std::string yuzu_token; + + // Add-Ons + std::map<u64, std::vector<std::string>> disabled_addons; } extern values; void Apply(); diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index a3b08c740..09ed74d78 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -103,13 +103,8 @@ bool VerifyLogin(const std::string& username, const std::string& token) { TelemetrySession::TelemetrySession() { #ifdef ENABLE_WEB_SERVICE - if (Settings::values.enable_telemetry) { - backend = std::make_unique<WebService::TelemetryJson>(Settings::values.web_api_url, - Settings::values.yuzu_username, - Settings::values.yuzu_token); - } else { - backend = std::make_unique<Telemetry::NullVisitor>(); - } + backend = std::make_unique<WebService::TelemetryJson>( + Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token); #else backend = std::make_unique<Telemetry::NullVisitor>(); #endif @@ -180,7 +175,8 @@ TelemetrySession::~TelemetrySession() { // This is just a placeholder to wrap up the session once the core completes and this is // destroyed. This will be moved elsewhere once we are actually doing real I/O with the service. field_collection.Accept(*backend); - backend->Complete(); + if (Settings::values.enable_telemetry) + backend->Complete(); backend = nullptr; } diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp deleted file mode 100644 index 8b9c548cc..000000000 --- a/src/video_core/command_processor.cpp +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2018 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <array> -#include <cstddef> -#include <memory> -#include <utility> -#include "common/assert.h" -#include "common/logging/log.h" -#include "common/microprofile.h" -#include "common/vector_math.h" -#include "core/memory.h" -#include "core/tracer/recorder.h" -#include "video_core/command_processor.h" -#include "video_core/engines/fermi_2d.h" -#include "video_core/engines/kepler_memory.h" -#include "video_core/engines/maxwell_3d.h" -#include "video_core/engines/maxwell_compute.h" -#include "video_core/engines/maxwell_dma.h" -#include "video_core/gpu.h" -#include "video_core/renderer_base.h" -#include "video_core/video_core.h" - -namespace Tegra { - -enum class BufferMethods { - BindObject = 0, - CountBufferMethods = 0x40, -}; - -MICROPROFILE_DEFINE(ProcessCommandLists, "GPU", "Execute command buffer", MP_RGB(128, 128, 192)); - -void GPU::ProcessCommandLists(const std::vector<CommandListHeader>& commands) { - MICROPROFILE_SCOPE(ProcessCommandLists); - - // On entering GPU code, assume all memory may be touched by the ARM core. - maxwell_3d->dirty_flags.OnMemoryWrite(); - - auto WriteReg = [this](u32 method, u32 subchannel, u32 value, u32 remaining_params) { - LOG_TRACE(HW_GPU, - "Processing method {:08X} on subchannel {} value " - "{:08X} remaining params {}", - method, subchannel, value, remaining_params); - - ASSERT(subchannel < bound_engines.size()); - - if (method == static_cast<u32>(BufferMethods::BindObject)) { - // Bind the current subchannel to the desired engine id. - LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", subchannel, value); - bound_engines[subchannel] = static_cast<EngineID>(value); - return; - } - - if (method < static_cast<u32>(BufferMethods::CountBufferMethods)) { - // TODO(Subv): Research and implement these methods. - LOG_ERROR(HW_GPU, "Special buffer methods other than Bind are not implemented"); - return; - } - - const EngineID engine = bound_engines[subchannel]; - - switch (engine) { - case EngineID::FERMI_TWOD_A: - fermi_2d->WriteReg(method, value); - break; - case EngineID::MAXWELL_B: - maxwell_3d->WriteReg(method, value, remaining_params); - break; - case EngineID::MAXWELL_COMPUTE_B: - maxwell_compute->WriteReg(method, value); - break; - case EngineID::MAXWELL_DMA_COPY_A: - maxwell_dma->WriteReg(method, value); - break; - case EngineID::KEPLER_INLINE_TO_MEMORY_B: - kepler_memory->WriteReg(method, value); - break; - default: - UNIMPLEMENTED_MSG("Unimplemented engine"); - } - }; - - for (auto entry : commands) { - Tegra::GPUVAddr address = entry.Address(); - u32 size = entry.sz; - const std::optional<VAddr> head_address = memory_manager->GpuToCpuAddress(address); - VAddr current_addr = *head_address; - while (current_addr < *head_address + size * sizeof(CommandHeader)) { - const CommandHeader header = {Memory::Read32(current_addr)}; - current_addr += sizeof(u32); - - switch (header.mode.Value()) { - case SubmissionMode::IncreasingOld: - case SubmissionMode::Increasing: { - // Increase the method value with each argument. - for (unsigned i = 0; i < header.arg_count; ++i) { - WriteReg(header.method + i, header.subchannel, Memory::Read32(current_addr), - header.arg_count - i - 1); - current_addr += sizeof(u32); - } - break; - } - case SubmissionMode::NonIncreasingOld: - case SubmissionMode::NonIncreasing: { - // Use the same method value for all arguments. - for (unsigned i = 0; i < header.arg_count; ++i) { - WriteReg(header.method, header.subchannel, Memory::Read32(current_addr), - header.arg_count - i - 1); - current_addr += sizeof(u32); - } - break; - } - case SubmissionMode::IncreaseOnce: { - ASSERT(header.arg_count.Value() >= 1); - - // Use the original method for the first argument and then the next method for all - // other arguments. - WriteReg(header.method, header.subchannel, Memory::Read32(current_addr), - header.arg_count - 1); - current_addr += sizeof(u32); - - for (unsigned i = 1; i < header.arg_count; ++i) { - WriteReg(header.method + 1, header.subchannel, Memory::Read32(current_addr), - header.arg_count - i - 1); - current_addr += sizeof(u32); - } - break; - } - case SubmissionMode::Inline: { - // The register value is stored in the bits 16-28 as an immediate - WriteReg(header.method, header.subchannel, header.inline_data, 0); - break; - } - default: - UNIMPLEMENTED(); - } - } - } -} - -} // namespace Tegra diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index b9faaf8e0..5ea094e64 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -1049,6 +1049,7 @@ union Instruction { BitField<49, 1, u64> nodep_flag; BitField<50, 3, u64> component_mask_selector; BitField<53, 4, u64> texture_info; + BitField<60, 1, u64> fp32_flag; TextureType GetTextureType() const { // The TEXS instruction has a weird encoding for the texture type. @@ -1549,7 +1550,7 @@ private: INST("1110111011011---", Id::STG, Type::Memory, "STG"), INST("110000----111---", Id::TEX, Type::Memory, "TEX"), INST("1101111101001---", Id::TXQ, Type::Memory, "TXQ"), - INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"), + INST("1101-00---------", Id::TEXS, Type::Memory, "TEXS"), INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"), INST("110010----111---", Id::TLD4, Type::Memory, "TLD4"), INST("1101111100------", Id::TLD4S, Type::Memory, "TLD4S"), diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index fd1242333..88c45a423 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -141,6 +141,12 @@ void GPU::CallMethod(const MethodCall& method_call) { return; } + if (method_call.method < static_cast<u32>(BufferMethods::CountBufferMethods)) { + // TODO(Subv): Research and implement these methods. + LOG_ERROR(HW_GPU, "Special buffer methods other than Bind are not implemented"); + return; + } + const EngineID engine = bound_engines[method_call.subchannel]; switch (engine) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 9e93bd609..2b29fc45f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -79,6 +79,26 @@ struct DrawParameters { } }; +struct FramebufferCacheKey { + bool is_single_buffer = false; + bool stencil_enable = false; + + std::array<GLenum, Maxwell::NumRenderTargets> color_attachments{}; + std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> colors{}; + u32 colors_count = 0; + + GLuint zeta = 0; + + auto Tie() const { + return std::tie(is_single_buffer, stencil_enable, color_attachments, colors, colors_count, + zeta); + } + + bool operator<(const FramebufferCacheKey& rhs) const { + return Tie() < rhs.Tie(); + } +}; + RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info) : res_cache{*this}, shader_cache{*this}, emu_window{window}, screen_info{info}, buffer_cache(*this, STREAM_BUFFER_SIZE) { @@ -90,9 +110,6 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo OpenGLState::ApplyDefaultState(); - // Create render framebuffer - framebuffer.Create(); - shader_program_manager = std::make_unique<GLShader::ProgramManager>(); state.draw.shader_program = 0; state.Apply(); @@ -361,6 +378,44 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { SyncClipEnabled(clip_distances); } +void RasterizerOpenGL::SetupCachedFramebuffer(const FramebufferCacheKey& fbkey, + OpenGLState& current_state) { + const auto [entry, is_cache_miss] = framebuffer_cache.try_emplace(fbkey); + auto& framebuffer = entry->second; + + if (is_cache_miss) + framebuffer.Create(); + + current_state.draw.draw_framebuffer = framebuffer.handle; + current_state.ApplyFramebufferState(); + + if (!is_cache_miss) + return; + + if (fbkey.is_single_buffer) { + if (fbkey.color_attachments[0] != GL_NONE) { + glFramebufferTexture(GL_DRAW_FRAMEBUFFER, fbkey.color_attachments[0], fbkey.colors[0], + 0); + } + glDrawBuffer(fbkey.color_attachments[0]); + } else { + for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { + if (fbkey.colors[index]) { + glFramebufferTexture(GL_DRAW_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), + fbkey.colors[index], 0); + } + } + glDrawBuffers(fbkey.colors_count, fbkey.color_attachments.data()); + } + + if (fbkey.zeta) { + GLenum zeta_attachment = + fbkey.stencil_enable ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT; + glFramebufferTexture(GL_DRAW_FRAMEBUFFER, zeta_attachment, fbkey.zeta, 0); + } +} + std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; @@ -444,10 +499,10 @@ void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool us UNIMPLEMENTED_IF(regs.rt_separate_frag_data != 0); // Bind the framebuffer surfaces - current_state.draw.draw_framebuffer = framebuffer.handle; - current_state.ApplyFramebufferState(); current_state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0; + FramebufferCacheKey fbkey; + if (using_color_fb) { if (single_color_target) { // Used when just a single color attachment is enabled, e.g. for clearing a color buffer @@ -463,14 +518,12 @@ void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool us state.framebuffer_srgb.enabled |= color_surface->GetSurfaceParams().srgb_conversion; } - glFramebufferTexture2D( - GL_DRAW_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target), GL_TEXTURE_2D, - color_surface != nullptr ? color_surface->Texture().handle : 0, 0); - glDrawBuffer(GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target)); + fbkey.is_single_buffer = true; + fbkey.color_attachments[0] = + GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target); + fbkey.colors[0] = color_surface != nullptr ? color_surface->Texture().handle : 0; } else { // Multiple color attachments are enabled - std::array<GLenum, Maxwell::NumRenderTargets> buffers; for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { Surface color_surface = res_cache.GetColorBufferSurface(index, preserve_contents); @@ -485,22 +538,17 @@ void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool us color_surface->GetSurfaceParams().srgb_conversion; } - buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index); - glFramebufferTexture2D( - GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), - GL_TEXTURE_2D, color_surface != nullptr ? color_surface->Texture().handle : 0, - 0); + fbkey.color_attachments[index] = + GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index); + fbkey.colors[index] = + color_surface != nullptr ? color_surface->Texture().handle : 0; } - glDrawBuffers(regs.rt_control.count, buffers.data()); + fbkey.is_single_buffer = false; + fbkey.colors_count = regs.rt_control.count; } } else { - // No color attachments are enabled - zero out all of them - for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), GL_TEXTURE_2D, - 0, 0); - } - glDrawBuffer(GL_NONE); + // No color attachments are enabled - leave them as zero + fbkey.is_single_buffer = true; } if (depth_surface) { @@ -508,22 +556,12 @@ void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool us // the shader doesn't actually write to it. depth_surface->MarkAsModified(true, res_cache); - if (regs.stencil_enable) { - // Attach both depth and stencil - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, - depth_surface->Texture().handle, 0); - } else { - // Attach depth - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - depth_surface->Texture().handle, 0); - // Clear stencil attachment - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); - } - } else { - // Clear both depth and stencil attachment - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, - 0); + fbkey.zeta = depth_surface->Texture().handle; + fbkey.stencil_enable = regs.stencil_enable; } + + SetupCachedFramebuffer(fbkey, current_state); + SyncViewport(current_state); } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 988fa3e27..8a891ffc7 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -40,6 +40,7 @@ namespace OpenGL { struct ScreenInfo; struct DrawParameters; +struct FramebufferCacheKey; class RasterizerOpenGL : public VideoCore::RasterizerInterface { public: @@ -195,11 +196,12 @@ private: OGLVertexArray> vertex_array_cache; + std::map<FramebufferCacheKey, OGLFramebuffer> framebuffer_cache; + std::array<SamplerInfo, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_samplers; static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; OGLBufferCache buffer_cache; - OGLFramebuffer framebuffer; PrimitiveAssembler primitive_assembler{buffer_cache}; GLint uniform_buffer_alignment; @@ -214,6 +216,8 @@ private: void SetupShaders(GLenum primitive_mode); + void SetupCachedFramebuffer(const FramebufferCacheKey& fbkey, OpenGLState& current_state); + enum class AccelDraw { Disabled, Arrays, Indexed }; AccelDraw accelerate_draw = AccelDraw::Disabled; diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 038b25c75..a4265f498 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -2,7 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <boost/functional/hash.hpp> #include "common/assert.h" +#include "common/hash.h" #include "core/core.h" #include "core/memory.h" #include "video_core/engines/maxwell_3d.h" @@ -66,14 +68,17 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type) // stage here. setup.SetProgramB(GetShaderCode(GetShaderAddress(Maxwell::ShaderProgram::VertexB))); case Maxwell::ShaderProgram::VertexB: + CalculateProperties(); program_result = GLShader::GenerateVertexShader(setup); gl_type = GL_VERTEX_SHADER; break; case Maxwell::ShaderProgram::Geometry: + CalculateProperties(); program_result = GLShader::GenerateGeometryShader(setup); gl_type = GL_GEOMETRY_SHADER; break; case Maxwell::ShaderProgram::Fragment: + CalculateProperties(); program_result = GLShader::GenerateFragmentShader(setup); gl_type = GL_FRAGMENT_SHADER; break; @@ -140,6 +145,46 @@ GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program, return target_program.handle; }; +static bool IsSchedInstruction(u32 offset, u32 main_offset) { + // sched instructions appear once every 4 instructions. + static constexpr std::size_t SchedPeriod = 4; + const std::size_t absolute_offset = offset - main_offset; + return (absolute_offset % SchedPeriod) == 0; +} + +static std::size_t CalculateProgramSize(const GLShader::ProgramCode& program) { + const std::size_t start_offset = 10; + std::size_t offset = start_offset; + std::size_t size = start_offset * sizeof(u64); + while (offset < program.size()) { + const u64 inst = program[offset]; + if (!IsSchedInstruction(offset, start_offset)) { + if (inst == 0 || (inst >> 52) == 0x50b) { + break; + } + } + size += 8; + offset++; + } + return size; +} + +void CachedShader::CalculateProperties() { + setup.program.real_size = CalculateProgramSize(setup.program.code); + setup.program.real_size_b = 0; + setup.program.unique_identifier = Common::CityHash64( + reinterpret_cast<const char*>(setup.program.code.data()), setup.program.real_size); + if (program_type == Maxwell::ShaderProgram::VertexA) { + std::size_t seed = 0; + boost::hash_combine(seed, setup.program.unique_identifier); + setup.program.real_size_b = CalculateProgramSize(setup.program.code_b); + const u64 identifier_b = Common::CityHash64( + reinterpret_cast<const char*>(setup.program.code_b.data()), setup.program.real_size_b); + boost::hash_combine(seed, identifier_b); + setup.program.unique_identifier = static_cast<u64>(seed); + } +} + ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer) : RasterizerCache{rasterizer} {} Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index 08f470de3..b4ef6030d 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h @@ -81,6 +81,8 @@ private: GLuint LazyGeometryProgram(OGLProgram& target_program, const std::string& glsl_topology, u32 max_vertices, const std::string& debug_name); + void CalculateProperties(); + VAddr addr; std::size_t shader_length; Maxwell::ShaderProgram program_type; diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 0c1632bd1..e7057a9cb 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -50,6 +50,14 @@ public: using std::runtime_error::runtime_error; }; +/// Generates code to use for a swizzle operation. +static std::string GetSwizzle(u64 elem) { + ASSERT(elem <= 3); + std::string swizzle = "."; + swizzle += "xyzw"[elem]; + return swizzle; +} + /// Translate topology static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) { switch (topology) { @@ -201,14 +209,53 @@ private: } }; +template <typename T> +class ShaderScopedScope { +public: + explicit ShaderScopedScope(T& writer, std::string_view begin_expr, std::string end_expr) + : writer(writer), end_expr(std::move(end_expr)) { + + if (begin_expr.empty()) { + writer.AddLine('{'); + } else { + writer.AddExpression(begin_expr); + writer.AddLine(" {"); + } + ++writer.scope; + } + + ShaderScopedScope(const ShaderScopedScope&) = delete; + + ~ShaderScopedScope() { + --writer.scope; + if (end_expr.empty()) { + writer.AddLine('}'); + } else { + writer.AddExpression("} "); + writer.AddExpression(end_expr); + writer.AddLine(';'); + } + } + + ShaderScopedScope& operator=(const ShaderScopedScope&) = delete; + +private: + T& writer; + std::string end_expr; +}; + class ShaderWriter { public: - void AddLine(std::string_view text) { + void AddExpression(std::string_view text) { DEBUG_ASSERT(scope >= 0); if (!text.empty()) { AppendIndentation(); } shader_source += text; + } + + void AddLine(std::string_view text) { + AddExpression(text); AddNewLine(); } @@ -228,6 +275,11 @@ public: return std::move(shader_source); } + ShaderScopedScope<ShaderWriter> Scope(std::string_view begin_expr = {}, + std::string end_expr = {}) { + return ShaderScopedScope(*this, begin_expr, end_expr); + } + int scope = 0; private: @@ -311,7 +363,7 @@ public: // Default - do nothing return value; default: - UNIMPLEMENTED_MSG("Unimplemented conversion size: {}", static_cast<u32>(size)); + UNREACHABLE_MSG("Unimplemented conversion size: {}", static_cast<u32>(size)); } } @@ -816,14 +868,12 @@ private: } if (precise && stage != Maxwell3D::Regs::ShaderStage::Fragment) { - shader.AddLine('{'); - ++shader.scope; + const auto scope = shader.Scope(); + // This avoids optimizations of constant propagation and keeps the code as the original // Sadly using the precise keyword causes "linking" errors on fragment shaders. shader.AddLine("precise float tmp = " + src + ';'); shader.AddLine(dest + " = tmp;"); - --shader.scope; - shader.AddLine('}'); } else { shader.AddLine(dest + " = " + src + ';'); } @@ -962,14 +1012,6 @@ private: } } - /// Generates code to use for a swizzle operation. - static std::string GetSwizzle(u64 elem) { - ASSERT(elem <= 3); - std::string swizzle = "."; - swizzle += "xyzw"[elem]; - return swizzle; - } - ShaderWriter& shader; ShaderWriter& declarations; std::vector<GLSLRegister> regs; @@ -1301,15 +1343,7 @@ private: regs.SetRegisterToInteger(dest, true, 0, result, 1, 1); } - void WriteTexsInstruction(const Instruction& instr, const std::string& coord, - const std::string& texture) { - // Add an extra scope and declare the texture coords inside to prevent - // overwriting them in case they are used as outputs of the texs instruction. - shader.AddLine('{'); - ++shader.scope; - shader.AddLine(coord); - shader.AddLine("vec4 texture_tmp = " + texture + ';'); - + void WriteTexsInstructionFloat(const Instruction& instr, const std::string& texture) { // TEXS has two destination registers and a swizzle. The first two elements in the swizzle // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1 @@ -1321,19 +1355,49 @@ private: if (written_components < 2) { // Write the first two swizzle components to gpr0 and gpr0+1 - regs.SetRegisterToFloat(instr.gpr0, component, "texture_tmp", 1, 4, false, + regs.SetRegisterToFloat(instr.gpr0, component, texture, 1, 4, false, written_components % 2); } else { ASSERT(instr.texs.HasTwoDestinations()); // Write the rest of the swizzle components to gpr28 and gpr28+1 - regs.SetRegisterToFloat(instr.gpr28, component, "texture_tmp", 1, 4, false, + regs.SetRegisterToFloat(instr.gpr28, component, texture, 1, 4, false, written_components % 2); } ++written_components; } - --shader.scope; - shader.AddLine('}'); + } + + void WriteTexsInstructionHalfFloat(const Instruction& instr, const std::string& texture) { + // TEXS.F16 destionation registers are packed in two registers in pairs (just like any half + // float instruction). + + std::array<std::string, 4> components; + u32 written_components = 0; + + for (u32 component = 0; component < 4; ++component) { + if (!instr.texs.IsComponentEnabled(component)) + continue; + components[written_components++] = texture + GetSwizzle(component); + } + if (written_components == 0) + return; + + const auto BuildComponent = [&](std::string low, std::string high, bool high_enabled) { + return "vec2(" + low + ", " + (high_enabled ? high : "0") + ')'; + }; + + regs.SetRegisterToHalfFloat( + instr.gpr0, 0, BuildComponent(components[0], components[1], written_components > 1), + Tegra::Shader::HalfMerge::H0_H1, 1, 1); + + if (written_components > 2) { + ASSERT(instr.texs.HasTwoDestinations()); + regs.SetRegisterToHalfFloat( + instr.gpr28, 0, + BuildComponent(components[2], components[3], written_components > 3), + Tegra::Shader::HalfMerge::H0_H1, 1, 1); + } } static u32 TextureCoordinates(Tegra::Shader::TextureType texture_type) { @@ -1356,12 +1420,10 @@ private: * top. */ void EmitPushToFlowStack(u32 target) { - shader.AddLine('{'); - ++shader.scope; + const auto scope = shader.Scope(); + shader.AddLine("flow_stack[flow_stack_top] = " + std::to_string(target) + "u;"); shader.AddLine("flow_stack_top++;"); - --shader.scope; - shader.AddLine('}'); } /* @@ -1369,13 +1431,11 @@ private: * popped address and decrementing the stack top. */ void EmitPopFromFlowStack() { - shader.AddLine('{'); - ++shader.scope; + const auto scope = shader.Scope(); + shader.AddLine("flow_stack_top--;"); shader.AddLine("jmp_to = flow_stack[flow_stack_top];"); shader.AddLine("break;"); - --shader.scope; - shader.AddLine('}'); } /// Writes the output values from a fragment shader to the corresponding GLSL output variables. @@ -1487,6 +1547,254 @@ private: } } + std::pair<size_t, std::string> ValidateAndGetCoordinateElement( + const Tegra::Shader::TextureType texture_type, const bool depth_compare, + const bool is_array, const bool lod_bias_enabled, size_t max_coords, size_t max_inputs) { + const size_t coord_count = TextureCoordinates(texture_type); + + size_t total_coord_count = coord_count + (is_array ? 1 : 0) + (depth_compare ? 1 : 0); + const size_t total_reg_count = total_coord_count + (lod_bias_enabled ? 1 : 0); + if (total_coord_count > max_coords || total_reg_count > max_inputs) { + UNIMPLEMENTED_MSG("Unsupported Texture operation"); + total_coord_count = std::min(total_coord_count, max_coords); + } + // 1D.DC opengl is using a vec3 but 2nd component is ignored later. + total_coord_count += + (depth_compare && !is_array && texture_type == Tegra::Shader::TextureType::Texture1D) + ? 1 + : 0; + + constexpr std::array<const char*, 5> coord_container{ + {"", "float coord = (", "vec2 coord = vec2(", "vec3 coord = vec3(", + "vec4 coord = vec4("}}; + + return std::pair<size_t, std::string>(coord_count, coord_container[total_coord_count]); + } + + std::string GetTextureCode(const Tegra::Shader::Instruction& instr, + const Tegra::Shader::TextureType texture_type, + const Tegra::Shader::TextureProcessMode process_mode, + const bool depth_compare, const bool is_array, + const size_t bias_offset) { + + if ((texture_type == Tegra::Shader::TextureType::Texture3D && + (is_array || depth_compare)) || + (texture_type == Tegra::Shader::TextureType::TextureCube && is_array && + depth_compare)) { + UNIMPLEMENTED_MSG("This method is not supported."); + } + + const std::string sampler = + GetSampler(instr.sampler, texture_type, is_array, depth_compare); + + const bool lod_needed = process_mode == Tegra::Shader::TextureProcessMode::LZ || + process_mode == Tegra::Shader::TextureProcessMode::LL || + process_mode == Tegra::Shader::TextureProcessMode::LLA; + + const bool gl_lod_supported = !( + (texture_type == Tegra::Shader::TextureType::Texture2D && is_array && depth_compare) || + (texture_type == Tegra::Shader::TextureType::TextureCube && !is_array && + depth_compare)); + + const std::string read_method = lod_needed && gl_lod_supported ? "textureLod(" : "texture("; + std::string texture = read_method + sampler + ", coord"; + + if (process_mode != Tegra::Shader::TextureProcessMode::None) { + if (process_mode == Tegra::Shader::TextureProcessMode::LZ) { + if (gl_lod_supported) { + texture += ", 0"; + } else { + // Lod 0 is emulated by a big negative bias + // in scenarios that are not supported by glsl + texture += ", -1000"; + } + } else { + // If present, lod or bias are always stored in the register indexed by the + // gpr20 + // field with an offset depending on the usage of the other registers + texture += ',' + regs.GetRegisterAsFloat(instr.gpr20.Value() + bias_offset); + } + } + texture += ")"; + return texture; + } + + std::pair<std::string, std::string> GetTEXCode( + const Instruction& instr, const Tegra::Shader::TextureType texture_type, + const Tegra::Shader::TextureProcessMode process_mode, const bool depth_compare, + const bool is_array) { + const bool lod_bias_enabled = (process_mode != Tegra::Shader::TextureProcessMode::None && + process_mode != Tegra::Shader::TextureProcessMode::LZ); + + const auto [coord_count, coord_dcl] = ValidateAndGetCoordinateElement( + texture_type, depth_compare, is_array, lod_bias_enabled, 4, 5); + // If enabled arrays index is always stored in the gpr8 field + const u64 array_register = instr.gpr8.Value(); + // First coordinate index is the gpr8 or gpr8 + 1 when arrays are used + const u64 coord_register = array_register + (is_array ? 1 : 0); + + std::string coord = coord_dcl; + for (size_t i = 0; i < coord_count;) { + coord += regs.GetRegisterAsFloat(coord_register + i); + ++i; + if (i != coord_count) { + coord += ','; + } + } + // 1D.DC in opengl the 2nd component is ignored. + if (depth_compare && !is_array && texture_type == Tegra::Shader::TextureType::Texture1D) { + coord += ",0.0"; + } + if (depth_compare) { + // Depth is always stored in the register signaled by gpr20 + // or in the next register if lod or bias are used + const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0); + coord += ',' + regs.GetRegisterAsFloat(depth_register); + } + if (is_array) { + coord += ',' + regs.GetRegisterAsInteger(array_register); + } + coord += ");"; + return std::make_pair( + coord, GetTextureCode(instr, texture_type, process_mode, depth_compare, is_array, 0)); + } + + std::pair<std::string, std::string> GetTEXSCode( + const Instruction& instr, const Tegra::Shader::TextureType texture_type, + const Tegra::Shader::TextureProcessMode process_mode, const bool depth_compare, + const bool is_array) { + const bool lod_bias_enabled = (process_mode != Tegra::Shader::TextureProcessMode::None && + process_mode != Tegra::Shader::TextureProcessMode::LZ); + + const auto [coord_count, coord_dcl] = ValidateAndGetCoordinateElement( + texture_type, depth_compare, is_array, lod_bias_enabled, 4, 4); + // If enabled arrays index is always stored in the gpr8 field + const u64 array_register = instr.gpr8.Value(); + // First coordinate index is stored in gpr8 field or (gpr8 + 1) when arrays are used + const u64 coord_register = array_register + (is_array ? 1 : 0); + const u64 last_coord_register = + (is_array || !(lod_bias_enabled || depth_compare) || (coord_count > 2)) + ? static_cast<u64>(instr.gpr20.Value()) + : coord_register + 1; + + std::string coord = coord_dcl; + for (size_t i = 0; i < coord_count; ++i) { + const bool last = (i == (coord_count - 1)) && (coord_count > 1); + coord += regs.GetRegisterAsFloat(last ? last_coord_register : coord_register + i); + if (i < coord_count - 1) { + coord += ','; + } + } + + if (depth_compare) { + // Depth is always stored in the register signaled by gpr20 + // or in the next register if lod or bias are used + const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0); + coord += ',' + regs.GetRegisterAsFloat(depth_register); + } + if (is_array) { + coord += ',' + regs.GetRegisterAsInteger(array_register); + } + coord += ");"; + + return std::make_pair(coord, + GetTextureCode(instr, texture_type, process_mode, depth_compare, + is_array, (coord_count > 2 ? 1 : 0))); + } + + std::pair<std::string, std::string> GetTLD4Code(const Instruction& instr, + const Tegra::Shader::TextureType texture_type, + const bool depth_compare, const bool is_array) { + + const size_t coord_count = TextureCoordinates(texture_type); + const size_t total_coord_count = coord_count + (is_array ? 1 : 0); + const size_t total_reg_count = total_coord_count + (depth_compare ? 1 : 0); + + constexpr std::array<const char*, 5> coord_container{ + {"", "", "vec2 coord = vec2(", "vec3 coord = vec3(", "vec4 coord = vec4("}}; + + // If enabled arrays index is always stored in the gpr8 field + const u64 array_register = instr.gpr8.Value(); + // First coordinate index is the gpr8 or gpr8 + 1 when arrays are used + const u64 coord_register = array_register + (is_array ? 1 : 0); + + std::string coord = coord_container[total_coord_count]; + for (size_t i = 0; i < coord_count;) { + coord += regs.GetRegisterAsFloat(coord_register + i); + ++i; + if (i != coord_count) { + coord += ','; + } + } + + if (is_array) { + coord += ',' + regs.GetRegisterAsInteger(array_register); + } + coord += ");"; + + const std::string sampler = + GetSampler(instr.sampler, texture_type, is_array, depth_compare); + + std::string texture = "textureGather(" + sampler + ", coord, "; + if (depth_compare) { + // Depth is always stored in the register signaled by gpr20 + texture += regs.GetRegisterAsFloat(instr.gpr20.Value()) + ')'; + } else { + texture += std::to_string(instr.tld4.component) + ')'; + } + return std::make_pair(coord, texture); + } + + std::pair<std::string, std::string> GetTLDSCode(const Instruction& instr, + const Tegra::Shader::TextureType texture_type, + const bool is_array) { + + const size_t coord_count = TextureCoordinates(texture_type); + const size_t total_coord_count = coord_count + (is_array ? 1 : 0); + const bool lod_enabled = + instr.tlds.GetTextureProcessMode() == Tegra::Shader::TextureProcessMode::LL; + + constexpr std::array<const char*, 4> coord_container{ + {"", "int coord = (", "ivec2 coord = ivec2(", "ivec3 coord = ivec3("}}; + + std::string coord = coord_container[total_coord_count]; + + // If enabled arrays index is always stored in the gpr8 field + const u64 array_register = instr.gpr8.Value(); + + // if is array gpr20 is used + const u64 coord_register = is_array ? instr.gpr20.Value() : instr.gpr8.Value(); + + const u64 last_coord_register = + ((coord_count > 2) || (coord_count == 2 && !lod_enabled)) && !is_array + ? static_cast<u64>(instr.gpr20.Value()) + : coord_register + 1; + + for (size_t i = 0; i < coord_count; ++i) { + const bool last = (i == (coord_count - 1)) && (coord_count > 1); + coord += regs.GetRegisterAsInteger(last ? last_coord_register : coord_register + i); + if (i < coord_count - 1) { + coord += ','; + } + } + if (is_array) { + coord += ',' + regs.GetRegisterAsInteger(array_register); + } + coord += ");"; + + const std::string sampler = GetSampler(instr.sampler, texture_type, is_array, false); + + std::string texture = "texelFetch(" + sampler + ", coords"; + + if (lod_enabled) { + // When lod is used always is in grp20 + texture += ", " + regs.GetRegisterAsInteger(instr.gpr20) + ')'; + } else { + texture += ", 0)"; + } + return std::make_pair(coord, texture); + } + /** * Compiles a single instruction from Tegra to GLSL. * @param offset the offset of the Tegra shader instruction. @@ -2287,8 +2595,7 @@ private: UNIMPLEMENTED_IF(instr.conversion.selector); UNIMPLEMENTED_IF_MSG(instr.generates_cc, "Condition codes generation in I2F is not implemented"); - - std::string op_a{}; + std::string op_a; if (instr.is_b_gpr) { op_a = @@ -2444,10 +2751,7 @@ private: case OpCode::Id::LD_C: { UNIMPLEMENTED_IF(instr.ld_c.unknown != 0); - // Add an extra scope and declare the index register inside to prevent - // overwriting it in case it is used as an output of the LD instruction. - shader.AddLine("{"); - ++shader.scope; + const auto scope = shader.Scope(); shader.AddLine("uint index = (" + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + " / 4) & (MAX_CONSTBUFFER_ELEMENTS - 1);"); @@ -2473,19 +2777,13 @@ private: UNIMPLEMENTED_MSG("Unhandled type: {}", static_cast<unsigned>(instr.ld_c.type.Value())); } - - --shader.scope; - shader.AddLine("}"); break; } case OpCode::Id::LD_L: { UNIMPLEMENTED_IF_MSG(instr.ld_l.unknown == 1, "LD_L Unhandled mode: {}", static_cast<unsigned>(instr.ld_l.unknown.Value())); - // Add an extra scope and declare the index register inside to prevent - // overwriting it in case it is used as an output of the LD instruction. - shader.AddLine('{'); - ++shader.scope; + const auto scope = shader.Scope(); std::string op = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + " + " + std::to_string(instr.smem_imm.Value()) + ')'; @@ -2502,9 +2800,6 @@ private: UNIMPLEMENTED_MSG("LD_L Unhandled type: {}", static_cast<unsigned>(instr.ldst_sl.type.Value())); } - - --shader.scope; - shader.AddLine('}'); break; } case OpCode::Id::ST_A: { @@ -2539,10 +2834,7 @@ private: UNIMPLEMENTED_IF_MSG(instr.st_l.unknown == 0, "ST_L Unhandled mode: {}", static_cast<unsigned>(instr.st_l.unknown.Value())); - // Add an extra scope and declare the index register inside to prevent - // overwriting it in case it is used as an output of the LD instruction. - shader.AddLine('{'); - ++shader.scope; + const auto scope = shader.Scope(); std::string op = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + " + " + std::to_string(instr.smem_imm.Value()) + ')'; @@ -2557,179 +2849,28 @@ private: UNIMPLEMENTED_MSG("ST_L Unhandled type: {}", static_cast<unsigned>(instr.ldst_sl.type.Value())); } - - --shader.scope; - shader.AddLine('}'); break; } case OpCode::Id::TEX: { Tegra::Shader::TextureType texture_type{instr.tex.texture_type}; - std::string coord; const bool is_array = instr.tex.array != 0; - + const bool depth_compare = + instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); + const auto process_mode = instr.tex.GetTextureProcessMode(); UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), "NODEP is not implemented"); UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), "AOFFI is not implemented"); - const bool depth_compare = - instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); - u32 num_coordinates = TextureCoordinates(texture_type); - u32 start_index = 0; - std::string array_elem; - if (is_array) { - array_elem = regs.GetRegisterAsInteger(instr.gpr8); - start_index = 1; - } - const auto process_mode = instr.tex.GetTextureProcessMode(); - u32 start_index_b = 0; - std::string lod_value; - if (process_mode != Tegra::Shader::TextureProcessMode::LZ && - process_mode != Tegra::Shader::TextureProcessMode::None) { - start_index_b = 1; - lod_value = regs.GetRegisterAsFloat(instr.gpr20); - } - - std::string depth_value; - if (depth_compare) { - depth_value = regs.GetRegisterAsFloat(instr.gpr20.Value() + start_index_b); - } - - bool depth_compare_extra = false; + const auto [coord, texture] = + GetTEXCode(instr, texture_type, process_mode, depth_compare, is_array); - switch (num_coordinates) { - case 1: { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index); - if (is_array) { - if (depth_compare) { - coord = "vec3 coords = vec3(" + x + ", " + depth_value + ", " + - array_elem + ");"; - } else { - coord = "vec2 coords = vec2(" + x + ", " + array_elem + ");"; - } - } else { - if (depth_compare) { - coord = "vec2 coords = vec2(" + x + ", " + depth_value + ");"; - } else { - coord = "float coords = " + x + ';'; - } - } - break; - } - case 2: { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index); - const std::string y = - regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index + 1); - if (is_array) { - if (depth_compare) { - coord = "vec4 coords = vec4(" + x + ", " + y + ", " + depth_value + - ", " + array_elem + ");"; - } else { - coord = "vec3 coords = vec3(" + x + ", " + y + ", " + array_elem + ");"; - } - } else { - if (depth_compare) { - coord = - "vec3 coords = vec3(" + x + ", " + y + ", " + depth_value + ");"; - } else { - coord = "vec2 coords = vec2(" + x + ", " + y + ");"; - } - } - break; - } - case 3: { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index); - const std::string y = - regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index + 1); - const std::string z = - regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index + 2); - if (is_array) { - depth_compare_extra = depth_compare; - coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + - array_elem + ");"; - } else { - if (depth_compare) { - coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + - depth_value + ");"; - } else { - coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; - } - } - break; - } - default: - UNIMPLEMENTED_MSG("Unhandled coordinates number {}", - static_cast<u32>(num_coordinates)); - - // Fallback to interpreting as a 2D texture for now - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - coord = "vec2 coords = vec2(" + x + ", " + y + ");"; - texture_type = Tegra::Shader::TextureType::Texture2D; - } - - const std::string sampler = - GetSampler(instr.sampler, texture_type, is_array, depth_compare); - // Add an extra scope and declare the texture coords inside to prevent - // overwriting them in case they are used as outputs of the texs instruction. - - shader.AddLine('{'); - ++shader.scope; + const auto scope = shader.Scope(); shader.AddLine(coord); - std::string texture; - switch (instr.tex.GetTextureProcessMode()) { - case Tegra::Shader::TextureProcessMode::None: { - if (!depth_compare_extra) { - texture = "texture(" + sampler + ", coords)"; - } else { - texture = "texture(" + sampler + ", coords, " + depth_value + ')'; - } - break; - } - case Tegra::Shader::TextureProcessMode::LZ: { - if (!depth_compare_extra) { - texture = "textureLod(" + sampler + ", coords, 0.0)"; - } else { - texture = "texture(" + sampler + ", coords, " + depth_value + ')'; - } - break; - } - case Tegra::Shader::TextureProcessMode::LB: - case Tegra::Shader::TextureProcessMode::LBA: { - // TODO: Figure if A suffix changes the equation at all. - if (!depth_compare_extra) { - texture = "texture(" + sampler + ", coords, " + lod_value + ')'; - } else { - texture = "texture(" + sampler + ", coords, " + depth_value + ')'; - LOG_WARNING(HW_GPU, - "OpenGL Limitation: can't set bias value along depth compare"); - } - break; - } - case Tegra::Shader::TextureProcessMode::LL: - case Tegra::Shader::TextureProcessMode::LLA: { - // TODO: Figure if A suffix changes the equation at all. - if (!depth_compare_extra) { - texture = "textureLod(" + sampler + ", coords, " + lod_value + ')'; - } else { - texture = "texture(" + sampler + ", coords, " + depth_value + ')'; - LOG_WARNING(HW_GPU, - "OpenGL Limitation: can't set lod value along depth compare"); - } - break; - } - default: { - if (!depth_compare_extra) { - texture = "texture(" + sampler + ", coords)"; - } else { - texture = "texture(" + sampler + ", coords, " + depth_value + ')'; - } - UNIMPLEMENTED_MSG("Unhandled texture process mode {}", - static_cast<u32>(instr.tex.GetTextureProcessMode())); - } - } - if (!depth_compare) { + if (depth_compare) { + regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); + } else { shader.AddLine("vec4 texture_tmp = " + texture + ';'); std::size_t dest_elem{}; for (std::size_t elem = 0; elem < 4; ++elem) { @@ -2741,147 +2882,42 @@ private: dest_elem); ++dest_elem; } - } else { - regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); } - --shader.scope; - shader.AddLine('}'); break; } case OpCode::Id::TEXS: { Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()}; - bool is_array{instr.texs.IsArrayTexture()}; + const bool is_array{instr.texs.IsArrayTexture()}; + const bool depth_compare = + instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); + const auto process_mode = instr.texs.GetTextureProcessMode(); UNIMPLEMENTED_IF_MSG(instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), "NODEP is not implemented"); - const bool depth_compare = - instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); - u32 num_coordinates = TextureCoordinates(texture_type); - const auto process_mode = instr.texs.GetTextureProcessMode(); - std::string lod_value; - std::string coord; - u32 lod_offset = 0; - if (process_mode == Tegra::Shader::TextureProcessMode::LL) { - if (num_coordinates > 2) { - lod_value = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); - lod_offset = 2; - } else { - lod_value = regs.GetRegisterAsFloat(instr.gpr20); - lod_offset = 1; - } - } + const auto scope = shader.Scope(); - switch (num_coordinates) { - case 1: { - coord = "float coords = " + regs.GetRegisterAsFloat(instr.gpr8) + ';'; - break; - } - case 2: { - if (is_array) { - if (depth_compare) { - const std::string index = regs.GetRegisterAsInteger(instr.gpr8); - const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string y = regs.GetRegisterAsFloat(instr.gpr20); - const std::string z = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); - coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index + - ");"; - } else { - const std::string index = regs.GetRegisterAsInteger(instr.gpr8); - const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string y = regs.GetRegisterAsFloat(instr.gpr20); - coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");"; - } - } else { - if (lod_offset != 0) { - if (depth_compare) { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = - regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string z = - regs.GetRegisterAsFloat(instr.gpr20.Value() + lod_offset); - coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; - } else { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = - regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - coord = "vec2 coords = vec2(" + x + ", " + y + ");"; - } - } else { - if (depth_compare) { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = - regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string z = regs.GetRegisterAsFloat(instr.gpr20); - coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; - } else { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = regs.GetRegisterAsFloat(instr.gpr20); - coord = "vec2 coords = vec2(" + x + ", " + y + ");"; - } - } - } - break; - } - case 3: { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string z = regs.GetRegisterAsFloat(instr.gpr20); - coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; - break; - } - default: - UNIMPLEMENTED_MSG("Unhandled coordinates number {}", - static_cast<u32>(num_coordinates)); + auto [coord, texture] = + GetTEXSCode(instr, texture_type, process_mode, depth_compare, is_array); - // Fallback to interpreting as a 2D texture for now - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = regs.GetRegisterAsFloat(instr.gpr20); - coord = "vec2 coords = vec2(" + x + ", " + y + ");"; - texture_type = Tegra::Shader::TextureType::Texture2D; - is_array = false; - } - const std::string sampler = - GetSampler(instr.sampler, texture_type, is_array, depth_compare); - std::string texture; - switch (process_mode) { - case Tegra::Shader::TextureProcessMode::None: { - texture = "texture(" + sampler + ", coords)"; - break; - } - case Tegra::Shader::TextureProcessMode::LZ: { - if (depth_compare && is_array) { - texture = "texture(" + sampler + ", coords)"; - } else { - texture = "textureLod(" + sampler + ", coords, 0.0)"; - } - break; - } - case Tegra::Shader::TextureProcessMode::LL: { - texture = "textureLod(" + sampler + ", coords, " + lod_value + ')'; - break; - } - default: { - texture = "texture(" + sampler + ", coords)"; - UNIMPLEMENTED_MSG("Unhandled texture process mode {}", - static_cast<u32>(instr.texs.GetTextureProcessMode())); - } + shader.AddLine(coord); + + if (depth_compare) { + texture = "vec4(" + texture + ')'; } - if (!depth_compare) { - WriteTexsInstruction(instr, coord, texture); + shader.AddLine("vec4 texture_tmp = " + texture + ';'); + + if (instr.texs.fp32_flag) { + WriteTexsInstructionFloat(instr, "texture_tmp"); } else { - WriteTexsInstruction(instr, coord, "vec4(" + texture + ')'); + WriteTexsInstructionHalfFloat(instr, "texture_tmp"); } - break; } case OpCode::Id::TLDS: { const Tegra::Shader::TextureType texture_type{instr.tlds.GetTextureType()}; const bool is_array{instr.tlds.IsArrayTexture()}; - ASSERT(texture_type == Tegra::Shader::TextureType::Texture2D); - ASSERT(is_array == false); - UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), "NODEP is not implemented"); UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), @@ -2889,63 +2925,16 @@ private: UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ), "MZ is not implemented"); - u32 extra_op_offset = 0; + const auto [coord, texture] = GetTLDSCode(instr, texture_type, is_array); - // Scope to avoid variable name overlaps. - shader.AddLine('{'); - ++shader.scope; - std::string coords; + const auto scope = shader.Scope(); - switch (texture_type) { - case Tegra::Shader::TextureType::Texture1D: { - const std::string x = regs.GetRegisterAsInteger(instr.gpr8); - coords = "float coords = " + x + ';'; - break; - } - case Tegra::Shader::TextureType::Texture2D: { - UNIMPLEMENTED_IF_MSG(is_array, "Unhandled 2d array texture"); - - const std::string x = regs.GetRegisterAsInteger(instr.gpr8); - const std::string y = regs.GetRegisterAsInteger(instr.gpr20); - // shader.AddLine("ivec2 coords = ivec2(" + x + ", " + y + ");"); - coords = "ivec2 coords = ivec2(" + x + ", " + y + ");"; - extra_op_offset = 1; - break; - } - default: - UNIMPLEMENTED_MSG("Unhandled texture type {}", static_cast<u32>(texture_type)); - } - const std::string sampler = - GetSampler(instr.sampler, texture_type, is_array, false); - std::string texture = "texelFetch(" + sampler + ", coords, 0)"; - switch (instr.tlds.GetTextureProcessMode()) { - case Tegra::Shader::TextureProcessMode::LZ: { - texture = "texelFetch(" + sampler + ", coords, 0)"; - break; - } - case Tegra::Shader::TextureProcessMode::LL: { - shader.AddLine( - "float lod = " + - regs.GetRegisterAsInteger(instr.gpr20.Value() + extra_op_offset) + ';'); - texture = "texelFetch(" + sampler + ", coords, lod)"; - break; - } - default: { - texture = "texelFetch(" + sampler + ", coords, 0)"; - UNIMPLEMENTED_MSG("Unhandled texture process mode {}", - static_cast<u32>(instr.tlds.GetTextureProcessMode())); - } - } - WriteTexsInstruction(instr, coords, texture); - - --shader.scope; - shader.AddLine('}'); + shader.AddLine(coord); + shader.AddLine("vec4 texture_tmp = " + texture + ';'); + WriteTexsInstructionFloat(instr, "texture_tmp"); break; } case OpCode::Id::TLD4: { - ASSERT(instr.tld4.texture_type == Tegra::Shader::TextureType::Texture2D); - ASSERT(instr.tld4.array == 0); - std::string coord; UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), "NODEP is not implemented"); @@ -2955,64 +2944,30 @@ private: "NDV is not implemented"); UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP), "PTP is not implemented"); + + auto texture_type = instr.tld4.texture_type.Value(); const bool depth_compare = instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); - auto texture_type = instr.tld4.texture_type.Value(); - u32 num_coordinates = TextureCoordinates(texture_type); - if (depth_compare) - num_coordinates += 1; + const bool is_array = instr.tld4.array != 0; - // Add an extra scope and declare the texture coords inside to prevent - // overwriting them in case they are used as outputs of the texs instruction. - shader.AddLine('{'); - ++shader.scope; + const auto [coord, texture] = + GetTLD4Code(instr, texture_type, depth_compare, is_array); - switch (num_coordinates) { - case 2: { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); - break; - } - case 3: { - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); - shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"); - break; - } - default: - UNIMPLEMENTED_MSG("Unhandled coordinates number {}", - static_cast<u32>(num_coordinates)); - const std::string x = regs.GetRegisterAsFloat(instr.gpr8); - const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); - texture_type = Tegra::Shader::TextureType::Texture2D; - } - - const std::string sampler = - GetSampler(instr.sampler, texture_type, false, depth_compare); + const auto scope = shader.Scope(); - const std::string texture = "textureGather(" + sampler + ", coords, " + - std::to_string(instr.tld4.component) + ')'; + shader.AddLine(coord); + std::size_t dest_elem{}; - if (!depth_compare) { - shader.AddLine("vec4 texture_tmp = " + texture + ';'); - std::size_t dest_elem{}; - for (std::size_t elem = 0; elem < 4; ++elem) { - if (!instr.tex.IsComponentEnabled(elem)) { - // Skip disabled components - continue; - } - regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false, - dest_elem); - ++dest_elem; + shader.AddLine("vec4 texture_tmp = " + texture + ';'); + for (std::size_t elem = 0; elem < 4; ++elem) { + if (!instr.tex.IsComponentEnabled(elem)) { + // Skip disabled components + continue; } - } else { - regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); + regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false, + dest_elem); + ++dest_elem; } - --shader.scope; - shader.AddLine('}'); break; } case OpCode::Id::TLD4S: { @@ -3023,45 +2978,42 @@ private: instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), "AOFFI is not implemented"); - // Scope to avoid variable name overlaps. - shader.AddLine('{'); - ++shader.scope; + const auto scope = shader.Scope(); + std::string coords; const bool depth_compare = instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); - const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); - const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20); - // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction. + const std::string sampler = GetSampler( instr.sampler, Tegra::Shader::TextureType::Texture2D, false, depth_compare); - if (!depth_compare) { - coords = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; - } else { - // Note: TLD4S coordinate encoding works just like TEXS's - const std::string op_y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - coords = "vec3 coords = vec3(" + op_a + ", " + op_y + ", " + op_b + ");"; - } - const std::string texture = "textureGather(" + sampler + ", coords, " + - std::to_string(instr.tld4s.component) + ')'; + + const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); + coords = "vec2 coords = vec2(" + op_a + ", "; + std::string texture = "textureGather(" + sampler + ", coords, "; if (!depth_compare) { - WriteTexsInstruction(instr, coords, texture); + const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20); + coords += op_b + ");"; + texture += std::to_string(instr.tld4s.component) + ')'; } else { - WriteTexsInstruction(instr, coords, "vec4(" + texture + ')'); + const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); + const std::string op_c = regs.GetRegisterAsFloat(instr.gpr20); + coords += op_b + ");"; + texture += op_c + ')'; } - - --shader.scope; - shader.AddLine('}'); + shader.AddLine(coords); + shader.AddLine("vec4 texture_tmp = " + texture + ';'); + WriteTexsInstructionFloat(instr, "texture_tmp"); break; } case OpCode::Id::TXQ: { UNIMPLEMENTED_IF_MSG(instr.txq.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), "NODEP is not implemented"); - ++shader.scope; - shader.AddLine('{'); - // TODO: the new commits on the texture refactor, change the way samplers work. + const auto scope = shader.Scope(); + + // TODO: The new commits on the texture refactor, change the way samplers work. // Sadly, not all texture instructions specify the type of texture their sampler // uses. This must be fixed at a later instance. const std::string sampler = @@ -3072,7 +3024,8 @@ private: regs.GetRegisterAsInteger(instr.gpr8) + ')'; const std::string mip_level = "textureQueryLevels(" + sampler + ')'; shader.AddLine("ivec2 sizes = " + texture + ';'); - regs.SetRegisterToInteger(instr.gpr0, true, 0, "sizes.x", 1, 1); + + regs.SetRegisterToInteger(instr.gpr0.Value() + 0, true, 0, "sizes.x", 1, 1); regs.SetRegisterToInteger(instr.gpr0.Value() + 1, true, 0, "sizes.y", 1, 1); regs.SetRegisterToInteger(instr.gpr0.Value() + 2, true, 0, "0", 1, 1); regs.SetRegisterToInteger(instr.gpr0.Value() + 3, true, 0, mip_level, 1, 1); @@ -3083,8 +3036,6 @@ private: static_cast<u32>(instr.txq.query_type.Value())); } } - --shader.scope; - shader.AddLine('}'); break; } case OpCode::Id::TMML: { @@ -3099,17 +3050,18 @@ private: const std::string sampler = GetSampler(instr.sampler, texture_type, is_array, false); - // TODO: add coordinates for different samplers once other texture types are + const auto scope = shader.Scope(); + + // TODO: Add coordinates for different samplers once other texture types are // implemented. - std::string coord; switch (texture_type) { case Tegra::Shader::TextureType::Texture1D: { - coord = "float coords = " + x + ';'; + shader.AddLine("float coords = " + x + ';'); break; } case Tegra::Shader::TextureType::Texture2D: { const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - coord = "vec2 coords = vec2(" + x + ", " + y + ");"; + shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); break; } default: @@ -3117,22 +3069,15 @@ private: // Fallback to interpreting as a 2D texture for now const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); - coord = "vec2 coords = vec2(" + x + ", " + y + ");"; + shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); texture_type = Tegra::Shader::TextureType::Texture2D; } - // Add an extra scope and declare the texture coords inside to prevent - // overwriting them in case they are used as outputs of the texs instruction. - shader.AddLine('{'); - ++shader.scope; - shader.AddLine(coord); + const std::string texture = "textureQueryLod(" + sampler + ", coords)"; - const std::string tmp = "vec2 tmp = " + texture + "*vec2(256.0, 256.0);"; - shader.AddLine(tmp); + shader.AddLine("vec2 tmp = " + texture + " * vec2(256.0, 256.0);"); regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(tmp.y)", 1, 1); regs.SetRegisterToInteger(instr.gpr0.Value() + 1, false, 0, "uint(tmp.x)", 1, 1); - --shader.scope; - shader.AddLine('}'); break; } default: { @@ -3971,4 +3916,4 @@ std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u return {}; } -} // namespace OpenGL::GLShader::Decompiler +} // namespace OpenGL::GLShader::Decompiler
\ No newline at end of file diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 23ed91e27..5d0819dc5 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <fmt/format.h> #include "common/assert.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/renderer_opengl/gl_shader_decompiler.h" @@ -16,6 +17,8 @@ static constexpr u32 PROGRAM_OFFSET{10}; ProgramResult GenerateVertexShader(const ShaderSetup& setup) { std::string out = "#version 430 core\n"; out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; + const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); + out += "// Shader Unique Id: VS" + id + "\n\n"; out += Decompiler::GetCommonDeclarations(); out += R"( @@ -84,6 +87,8 @@ void main() { ProgramResult GenerateGeometryShader(const ShaderSetup& setup) { // Version is intentionally skipped in shader generation, it's added by the lazy compilation. std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; + const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); + out += "// Shader Unique Id: GS" + id + "\n\n"; out += Decompiler::GetCommonDeclarations(); out += "bool exec_geometry();\n"; @@ -117,6 +122,8 @@ void main() { ProgramResult GenerateFragmentShader(const ShaderSetup& setup) { std::string out = "#version 430 core\n"; out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; + const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); + out += "// Shader Unique Id: FS" + id + "\n\n"; out += Decompiler::GetCommonDeclarations(); out += "bool exec_fragment();\n"; diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index 4fa6d7612..fcc20d3b4 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h @@ -177,6 +177,9 @@ struct ShaderSetup { struct { ProgramCode code; ProgramCode code_b; // Used for dual vertex shaders + u64 unique_identifier; + std::size_t real_size; + std::size_t real_size_b; } program; /// Used in scenarios where we have a dual vertex shaders diff --git a/src/video_core/surface.h b/src/video_core/surface.h index 0dd3eb2e4..e23cfecbc 100644 --- a/src/video_core/surface.h +++ b/src/video_core/surface.h @@ -125,6 +125,75 @@ enum class SurfaceTarget { TextureCubeArray, }; +constexpr std::array<u32, MaxPixelFormat> compression_factor_table = {{ + 1, // ABGR8U + 1, // ABGR8S + 1, // ABGR8UI + 1, // B5G6R5U + 1, // A2B10G10R10U + 1, // A1B5G5R5U + 1, // R8U + 1, // R8UI + 1, // RGBA16F + 1, // RGBA16U + 1, // RGBA16UI + 1, // R11FG11FB10F + 1, // RGBA32UI + 4, // DXT1 + 4, // DXT23 + 4, // DXT45 + 4, // DXN1 + 4, // DXN2UNORM + 4, // DXN2SNORM + 4, // BC7U + 4, // BC6H_UF16 + 4, // BC6H_SF16 + 4, // ASTC_2D_4X4 + 1, // G8R8U + 1, // G8R8S + 1, // BGRA8 + 1, // RGBA32F + 1, // RG32F + 1, // R32F + 1, // R16F + 1, // R16U + 1, // R16S + 1, // R16UI + 1, // R16I + 1, // RG16 + 1, // RG16F + 1, // RG16UI + 1, // RG16I + 1, // RG16S + 1, // RGB32F + 1, // RGBA8_SRGB + 1, // RG8U + 1, // RG8S + 1, // RG32UI + 1, // R32UI + 4, // ASTC_2D_8X8 + 4, // ASTC_2D_8X5 + 4, // ASTC_2D_5X4 + 1, // BGRA8_SRGB + 4, // DXT1_SRGB + 4, // DXT23_SRGB + 4, // DXT45_SRGB + 4, // BC7U_SRGB + 4, // ASTC_2D_4X4_SRGB + 4, // ASTC_2D_8X8_SRGB + 4, // ASTC_2D_8X5_SRGB + 4, // ASTC_2D_5X4_SRGB + 4, // ASTC_2D_5X5 + 4, // ASTC_2D_5X5_SRGB + 4, // ASTC_2D_10X8 + 4, // ASTC_2D_10X8_SRGB + 1, // Z32F + 1, // Z16 + 1, // Z24S8 + 1, // S8Z24 + 1, // Z32FS8 +}}; + /** * Gets the compression factor for the specified PixelFormat. This applies to just the * "compressed width" and "compressed height", not the overall compression factor of a @@ -135,304 +204,237 @@ static constexpr u32 GetCompressionFactor(PixelFormat format) { if (format == PixelFormat::Invalid) return 0; - constexpr std::array<u32, MaxPixelFormat> compression_factor_table = {{ - 1, // ABGR8U - 1, // ABGR8S - 1, // ABGR8UI - 1, // B5G6R5U - 1, // A2B10G10R10U - 1, // A1B5G5R5U - 1, // R8U - 1, // R8UI - 1, // RGBA16F - 1, // RGBA16U - 1, // RGBA16UI - 1, // R11FG11FB10F - 1, // RGBA32UI - 4, // DXT1 - 4, // DXT23 - 4, // DXT45 - 4, // DXN1 - 4, // DXN2UNORM - 4, // DXN2SNORM - 4, // BC7U - 4, // BC6H_UF16 - 4, // BC6H_SF16 - 4, // ASTC_2D_4X4 - 1, // G8R8U - 1, // G8R8S - 1, // BGRA8 - 1, // RGBA32F - 1, // RG32F - 1, // R32F - 1, // R16F - 1, // R16U - 1, // R16S - 1, // R16UI - 1, // R16I - 1, // RG16 - 1, // RG16F - 1, // RG16UI - 1, // RG16I - 1, // RG16S - 1, // RGB32F - 1, // RGBA8_SRGB - 1, // RG8U - 1, // RG8S - 1, // RG32UI - 1, // R32UI - 4, // ASTC_2D_8X8 - 4, // ASTC_2D_8X5 - 4, // ASTC_2D_5X4 - 1, // BGRA8_SRGB - 4, // DXT1_SRGB - 4, // DXT23_SRGB - 4, // DXT45_SRGB - 4, // BC7U_SRGB - 4, // ASTC_2D_4X4_SRGB - 4, // ASTC_2D_8X8_SRGB - 4, // ASTC_2D_8X5_SRGB - 4, // ASTC_2D_5X4_SRGB - 4, // ASTC_2D_5X5 - 4, // ASTC_2D_5X5_SRGB - 4, // ASTC_2D_10X8 - 4, // ASTC_2D_10X8_SRGB - 1, // Z32F - 1, // Z16 - 1, // Z24S8 - 1, // S8Z24 - 1, // Z32FS8 - }}; - ASSERT(static_cast<std::size_t>(format) < compression_factor_table.size()); return compression_factor_table[static_cast<std::size_t>(format)]; } +constexpr std::array<u32, MaxPixelFormat> block_width_table = {{ + 1, // ABGR8U + 1, // ABGR8S + 1, // ABGR8UI + 1, // B5G6R5U + 1, // A2B10G10R10U + 1, // A1B5G5R5U + 1, // R8U + 1, // R8UI + 1, // RGBA16F + 1, // RGBA16U + 1, // RGBA16UI + 1, // R11FG11FB10F + 1, // RGBA32UI + 4, // DXT1 + 4, // DXT23 + 4, // DXT45 + 4, // DXN1 + 4, // DXN2UNORM + 4, // DXN2SNORM + 4, // BC7U + 4, // BC6H_UF16 + 4, // BC6H_SF16 + 4, // ASTC_2D_4X4 + 1, // G8R8U + 1, // G8R8S + 1, // BGRA8 + 1, // RGBA32F + 1, // RG32F + 1, // R32F + 1, // R16F + 1, // R16U + 1, // R16S + 1, // R16UI + 1, // R16I + 1, // RG16 + 1, // RG16F + 1, // RG16UI + 1, // RG16I + 1, // RG16S + 1, // RGB32F + 1, // RGBA8_SRGB + 1, // RG8U + 1, // RG8S + 1, // RG32UI + 1, // R32UI + 8, // ASTC_2D_8X8 + 8, // ASTC_2D_8X5 + 5, // ASTC_2D_5X4 + 1, // BGRA8_SRGB + 4, // DXT1_SRGB + 4, // DXT23_SRGB + 4, // DXT45_SRGB + 4, // BC7U_SRGB + 4, // ASTC_2D_4X4_SRGB + 8, // ASTC_2D_8X8_SRGB + 8, // ASTC_2D_8X5_SRGB + 5, // ASTC_2D_5X4_SRGB + 5, // ASTC_2D_5X5 + 5, // ASTC_2D_5X5_SRGB + 10, // ASTC_2D_10X8 + 10, // ASTC_2D_10X8_SRGB + 1, // Z32F + 1, // Z16 + 1, // Z24S8 + 1, // S8Z24 + 1, // Z32FS8 +}}; + static constexpr u32 GetDefaultBlockWidth(PixelFormat format) { if (format == PixelFormat::Invalid) return 0; - constexpr std::array<u32, MaxPixelFormat> block_width_table = {{ - 1, // ABGR8U - 1, // ABGR8S - 1, // ABGR8UI - 1, // B5G6R5U - 1, // A2B10G10R10U - 1, // A1B5G5R5U - 1, // R8U - 1, // R8UI - 1, // RGBA16F - 1, // RGBA16U - 1, // RGBA16UI - 1, // R11FG11FB10F - 1, // RGBA32UI - 4, // DXT1 - 4, // DXT23 - 4, // DXT45 - 4, // DXN1 - 4, // DXN2UNORM - 4, // DXN2SNORM - 4, // BC7U - 4, // BC6H_UF16 - 4, // BC6H_SF16 - 4, // ASTC_2D_4X4 - 1, // G8R8U - 1, // G8R8S - 1, // BGRA8 - 1, // RGBA32F - 1, // RG32F - 1, // R32F - 1, // R16F - 1, // R16U - 1, // R16S - 1, // R16UI - 1, // R16I - 1, // RG16 - 1, // RG16F - 1, // RG16UI - 1, // RG16I - 1, // RG16S - 1, // RGB32F - 1, // RGBA8_SRGB - 1, // RG8U - 1, // RG8S - 1, // RG32UI - 1, // R32UI - 8, // ASTC_2D_8X8 - 8, // ASTC_2D_8X5 - 5, // ASTC_2D_5X4 - 1, // BGRA8_SRGB - 4, // DXT1_SRGB - 4, // DXT23_SRGB - 4, // DXT45_SRGB - 4, // BC7U_SRGB - 4, // ASTC_2D_4X4_SRGB - 8, // ASTC_2D_8X8_SRGB - 8, // ASTC_2D_8X5_SRGB - 5, // ASTC_2D_5X4_SRGB - 5, // ASTC_2D_5X5 - 5, // ASTC_2D_5X5_SRGB - 10, // ASTC_2D_10X8 - 10, // ASTC_2D_10X8_SRGB - 1, // Z32F - 1, // Z16 - 1, // Z24S8 - 1, // S8Z24 - 1, // Z32FS8 - }}; + ASSERT(static_cast<std::size_t>(format) < block_width_table.size()); return block_width_table[static_cast<std::size_t>(format)]; } +constexpr std::array<u32, MaxPixelFormat> block_height_table = {{ + 1, // ABGR8U + 1, // ABGR8S + 1, // ABGR8UI + 1, // B5G6R5U + 1, // A2B10G10R10U + 1, // A1B5G5R5U + 1, // R8U + 1, // R8UI + 1, // RGBA16F + 1, // RGBA16U + 1, // RGBA16UI + 1, // R11FG11FB10F + 1, // RGBA32UI + 4, // DXT1 + 4, // DXT23 + 4, // DXT45 + 4, // DXN1 + 4, // DXN2UNORM + 4, // DXN2SNORM + 4, // BC7U + 4, // BC6H_UF16 + 4, // BC6H_SF16 + 4, // ASTC_2D_4X4 + 1, // G8R8U + 1, // G8R8S + 1, // BGRA8 + 1, // RGBA32F + 1, // RG32F + 1, // R32F + 1, // R16F + 1, // R16U + 1, // R16S + 1, // R16UI + 1, // R16I + 1, // RG16 + 1, // RG16F + 1, // RG16UI + 1, // RG16I + 1, // RG16S + 1, // RGB32F + 1, // RGBA8_SRGB + 1, // RG8U + 1, // RG8S + 1, // RG32UI + 1, // R32UI + 8, // ASTC_2D_8X8 + 5, // ASTC_2D_8X5 + 4, // ASTC_2D_5X4 + 1, // BGRA8_SRGB + 4, // DXT1_SRGB + 4, // DXT23_SRGB + 4, // DXT45_SRGB + 4, // BC7U_SRGB + 4, // ASTC_2D_4X4_SRGB + 8, // ASTC_2D_8X8_SRGB + 5, // ASTC_2D_8X5_SRGB + 4, // ASTC_2D_5X4_SRGB + 5, // ASTC_2D_5X5 + 5, // ASTC_2D_5X5_SRGB + 8, // ASTC_2D_10X8 + 8, // ASTC_2D_10X8_SRGB + 1, // Z32F + 1, // Z16 + 1, // Z24S8 + 1, // S8Z24 + 1, // Z32FS8 +}}; + static constexpr u32 GetDefaultBlockHeight(PixelFormat format) { if (format == PixelFormat::Invalid) return 0; - constexpr std::array<u32, MaxPixelFormat> block_height_table = {{ - 1, // ABGR8U - 1, // ABGR8S - 1, // ABGR8UI - 1, // B5G6R5U - 1, // A2B10G10R10U - 1, // A1B5G5R5U - 1, // R8U - 1, // R8UI - 1, // RGBA16F - 1, // RGBA16U - 1, // RGBA16UI - 1, // R11FG11FB10F - 1, // RGBA32UI - 4, // DXT1 - 4, // DXT23 - 4, // DXT45 - 4, // DXN1 - 4, // DXN2UNORM - 4, // DXN2SNORM - 4, // BC7U - 4, // BC6H_UF16 - 4, // BC6H_SF16 - 4, // ASTC_2D_4X4 - 1, // G8R8U - 1, // G8R8S - 1, // BGRA8 - 1, // RGBA32F - 1, // RG32F - 1, // R32F - 1, // R16F - 1, // R16U - 1, // R16S - 1, // R16UI - 1, // R16I - 1, // RG16 - 1, // RG16F - 1, // RG16UI - 1, // RG16I - 1, // RG16S - 1, // RGB32F - 1, // RGBA8_SRGB - 1, // RG8U - 1, // RG8S - 1, // RG32UI - 1, // R32UI - 8, // ASTC_2D_8X8 - 5, // ASTC_2D_8X5 - 4, // ASTC_2D_5X4 - 1, // BGRA8_SRGB - 4, // DXT1_SRGB - 4, // DXT23_SRGB - 4, // DXT45_SRGB - 4, // BC7U_SRGB - 4, // ASTC_2D_4X4_SRGB - 8, // ASTC_2D_8X8_SRGB - 5, // ASTC_2D_8X5_SRGB - 4, // ASTC_2D_5X4_SRGB - 5, // ASTC_2D_5X5 - 5, // ASTC_2D_5X5_SRGB - 8, // ASTC_2D_10X8 - 8, // ASTC_2D_10X8_SRGB - 1, // Z32F - 1, // Z16 - 1, // Z24S8 - 1, // S8Z24 - 1, // Z32FS8 - }}; - ASSERT(static_cast<std::size_t>(format) < block_height_table.size()); return block_height_table[static_cast<std::size_t>(format)]; } +constexpr std::array<u32, MaxPixelFormat> bpp_table = {{ + 32, // ABGR8U + 32, // ABGR8S + 32, // ABGR8UI + 16, // B5G6R5U + 32, // A2B10G10R10U + 16, // A1B5G5R5U + 8, // R8U + 8, // R8UI + 64, // RGBA16F + 64, // RGBA16U + 64, // RGBA16UI + 32, // R11FG11FB10F + 128, // RGBA32UI + 64, // DXT1 + 128, // DXT23 + 128, // DXT45 + 64, // DXN1 + 128, // DXN2UNORM + 128, // DXN2SNORM + 128, // BC7U + 128, // BC6H_UF16 + 128, // BC6H_SF16 + 128, // ASTC_2D_4X4 + 16, // G8R8U + 16, // G8R8S + 32, // BGRA8 + 128, // RGBA32F + 64, // RG32F + 32, // R32F + 16, // R16F + 16, // R16U + 16, // R16S + 16, // R16UI + 16, // R16I + 32, // RG16 + 32, // RG16F + 32, // RG16UI + 32, // RG16I + 32, // RG16S + 96, // RGB32F + 32, // RGBA8_SRGB + 16, // RG8U + 16, // RG8S + 64, // RG32UI + 32, // R32UI + 128, // ASTC_2D_8X8 + 128, // ASTC_2D_8X5 + 128, // ASTC_2D_5X4 + 32, // BGRA8_SRGB + 64, // DXT1_SRGB + 128, // DXT23_SRGB + 128, // DXT45_SRGB + 128, // BC7U + 128, // ASTC_2D_4X4_SRGB + 128, // ASTC_2D_8X8_SRGB + 128, // ASTC_2D_8X5_SRGB + 128, // ASTC_2D_5X4_SRGB + 128, // ASTC_2D_5X5 + 128, // ASTC_2D_5X5_SRGB + 128, // ASTC_2D_10X8 + 128, // ASTC_2D_10X8_SRGB + 32, // Z32F + 16, // Z16 + 32, // Z24S8 + 32, // S8Z24 + 64, // Z32FS8 +}}; + static constexpr u32 GetFormatBpp(PixelFormat format) { if (format == PixelFormat::Invalid) return 0; - constexpr std::array<u32, MaxPixelFormat> bpp_table = {{ - 32, // ABGR8U - 32, // ABGR8S - 32, // ABGR8UI - 16, // B5G6R5U - 32, // A2B10G10R10U - 16, // A1B5G5R5U - 8, // R8U - 8, // R8UI - 64, // RGBA16F - 64, // RGBA16U - 64, // RGBA16UI - 32, // R11FG11FB10F - 128, // RGBA32UI - 64, // DXT1 - 128, // DXT23 - 128, // DXT45 - 64, // DXN1 - 128, // DXN2UNORM - 128, // DXN2SNORM - 128, // BC7U - 128, // BC6H_UF16 - 128, // BC6H_SF16 - 128, // ASTC_2D_4X4 - 16, // G8R8U - 16, // G8R8S - 32, // BGRA8 - 128, // RGBA32F - 64, // RG32F - 32, // R32F - 16, // R16F - 16, // R16U - 16, // R16S - 16, // R16UI - 16, // R16I - 32, // RG16 - 32, // RG16F - 32, // RG16UI - 32, // RG16I - 32, // RG16S - 96, // RGB32F - 32, // RGBA8_SRGB - 16, // RG8U - 16, // RG8S - 64, // RG32UI - 32, // R32UI - 128, // ASTC_2D_8X8 - 128, // ASTC_2D_8X5 - 128, // ASTC_2D_5X4 - 32, // BGRA8_SRGB - 64, // DXT1_SRGB - 128, // DXT23_SRGB - 128, // DXT45_SRGB - 128, // BC7U - 128, // ASTC_2D_4X4_SRGB - 128, // ASTC_2D_8X8_SRGB - 128, // ASTC_2D_8X5_SRGB - 128, // ASTC_2D_5X4_SRGB - 128, // ASTC_2D_5X5 - 128, // ASTC_2D_5X5_SRGB - 128, // ASTC_2D_10X8 - 128, // ASTC_2D_10X8_SRGB - 32, // Z32F - 16, // Z16 - 32, // Z24S8 - 32, // S8Z24 - 64, // Z32FS8 - }}; - ASSERT(static_cast<std::size_t>(format) < bpp_table.size()); return bpp_table[static_cast<std::size_t>(format)]; } diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index cfca8f4a8..3232aa8fb 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -35,6 +35,8 @@ add_executable(yuzu configuration/configure_mouse_advanced.h configuration/configure_system.cpp configuration/configure_system.h + configuration/configure_per_general.cpp + configuration/configure_per_general.h configuration/configure_touchscreen_advanced.cpp configuration/configure_touchscreen_advanced.h configuration/configure_web.cpp @@ -86,6 +88,7 @@ set(UIS configuration/configure_input.ui configuration/configure_input_player.ui configuration/configure_mouse_advanced.ui + configuration/configure_per_general.ui configuration/configure_system.ui configuration/configure_touchscreen_advanced.ui configuration/configure_web.ui diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 83ebbd1fe..eb2077b0f 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -206,60 +206,57 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default void Config::ReadPlayerValues() { for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { - Settings::values.players[p].connected = - qt_config->value(QString("player_%1_connected").arg(p), false).toBool(); + auto& player = Settings::values.players[p]; - Settings::values.players[p].type = static_cast<Settings::ControllerType>( + player.connected = qt_config->value(QString("player_%1_connected").arg(p), false).toBool(); + + player.type = static_cast<Settings::ControllerType>( qt_config ->value(QString("player_%1_type").arg(p), static_cast<u8>(Settings::ControllerType::DualJoycon)) .toUInt()); - Settings::values.players[p].body_color_left = - qt_config - ->value(QString("player_%1_body_color_left").arg(p), - Settings::JOYCON_BODY_NEON_BLUE) - .toUInt(); - Settings::values.players[p].body_color_right = - qt_config - ->value(QString("player_%1_body_color_right").arg(p), - Settings::JOYCON_BODY_NEON_RED) - .toUInt(); - Settings::values.players[p].button_color_left = - qt_config - ->value(QString("player_%1_button_color_left").arg(p), - Settings::JOYCON_BUTTONS_NEON_BLUE) - .toUInt(); - Settings::values.players[p].button_color_right = - qt_config - ->value(QString("player_%1_button_color_right").arg(p), - Settings::JOYCON_BUTTONS_NEON_RED) - .toUInt(); + player.body_color_left = qt_config + ->value(QString("player_%1_body_color_left").arg(p), + Settings::JOYCON_BODY_NEON_BLUE) + .toUInt(); + player.body_color_right = qt_config + ->value(QString("player_%1_body_color_right").arg(p), + Settings::JOYCON_BODY_NEON_RED) + .toUInt(); + player.button_color_left = qt_config + ->value(QString("player_%1_button_color_left").arg(p), + Settings::JOYCON_BUTTONS_NEON_BLUE) + .toUInt(); + player.button_color_right = qt_config + ->value(QString("player_%1_button_color_right").arg(p), + Settings::JOYCON_BUTTONS_NEON_RED) + .toUInt(); for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); - Settings::values.players[p].buttons[i] = + player.buttons[i] = qt_config ->value(QString("player_%1_").arg(p) + Settings::NativeButton::mapping[i], QString::fromStdString(default_param)) .toString() .toStdString(); - if (Settings::values.players[p].buttons[i].empty()) - Settings::values.players[p].buttons[i] = default_param; + if (player.buttons[i].empty()) + player.buttons[i] = default_param; } for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { std::string default_param = InputCommon::GenerateAnalogParamFromKeys( default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], default_analogs[i][3], default_analogs[i][4], 0.5f); - Settings::values.players[p].analogs[i] = + player.analogs[i] = qt_config ->value(QString("player_%1_").arg(p) + Settings::NativeAnalog::mapping[i], QString::fromStdString(default_param)) .toString() .toStdString(); - if (Settings::values.players[p].analogs[i].empty()) - Settings::values.players[p].analogs[i] = default_param; + if (player.analogs[i].empty()) + player.analogs[i] = default_param; } } @@ -444,6 +441,21 @@ void Config::ReadValues() { Settings::values.yuzu_token = qt_config->value("yuzu_token").toString().toStdString(); qt_config->endGroup(); + const auto size = qt_config->beginReadArray("DisabledAddOns"); + for (int i = 0; i < size; ++i) { + qt_config->setArrayIndex(i); + const auto title_id = qt_config->value("title_id", 0).toULongLong(); + std::vector<std::string> out; + const auto d_size = qt_config->beginReadArray("disabled"); + for (int j = 0; j < d_size; ++j) { + qt_config->setArrayIndex(j); + out.push_back(qt_config->value("d", "").toString().toStdString()); + } + qt_config->endArray(); + Settings::values.disabled_addons.insert_or_assign(title_id, out); + } + qt_config->endArray(); + qt_config->beginGroup("UI"); UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString(); UISettings::values.enable_discord_presence = @@ -511,30 +523,28 @@ void Config::ReadValues() { } void Config::SavePlayerValues() { - for (int p = 0; p < Settings::values.players.size(); ++p) { - qt_config->setValue(QString("player_%1_connected").arg(p), - Settings::values.players[p].connected); - qt_config->setValue(QString("player_%1_type").arg(p), - static_cast<u8>(Settings::values.players[p].type)); - - qt_config->setValue(QString("player_%1_body_color_left").arg(p), - Settings::values.players[p].body_color_left); - qt_config->setValue(QString("player_%1_body_color_right").arg(p), - Settings::values.players[p].body_color_right); + for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { + const auto& player = Settings::values.players[p]; + + qt_config->setValue(QString("player_%1_connected").arg(p), player.connected); + qt_config->setValue(QString("player_%1_type").arg(p), static_cast<u8>(player.type)); + + qt_config->setValue(QString("player_%1_body_color_left").arg(p), player.body_color_left); + qt_config->setValue(QString("player_%1_body_color_right").arg(p), player.body_color_right); qt_config->setValue(QString("player_%1_button_color_left").arg(p), - Settings::values.players[p].button_color_left); + player.button_color_left); qt_config->setValue(QString("player_%1_button_color_right").arg(p), - Settings::values.players[p].button_color_right); + player.button_color_right); for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { qt_config->setValue(QString("player_%1_").arg(p) + QString::fromStdString(Settings::NativeButton::mapping[i]), - QString::fromStdString(Settings::values.players[p].buttons[i])); + QString::fromStdString(player.buttons[i])); } for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { qt_config->setValue(QString("player_%1_").arg(p) + QString::fromStdString(Settings::NativeAnalog::mapping[i]), - QString::fromStdString(Settings::values.players[p].analogs[i])); + QString::fromStdString(player.analogs[i])); } } } @@ -650,6 +660,21 @@ void Config::SaveValues() { qt_config->setValue("yuzu_token", QString::fromStdString(Settings::values.yuzu_token)); qt_config->endGroup(); + qt_config->beginWriteArray("DisabledAddOns"); + int i = 0; + for (const auto& elem : Settings::values.disabled_addons) { + qt_config->setArrayIndex(i); + qt_config->setValue("title_id", QVariant::fromValue<u64>(elem.first)); + qt_config->beginWriteArray("disabled"); + for (std::size_t j = 0; j < elem.second.size(); ++j) { + qt_config->setArrayIndex(j); + qt_config->setValue("d", QString::fromStdString(elem.second[j])); + } + qt_config->endArray(); + ++i; + } + qt_config->endArray(); + qt_config->beginGroup("UI"); qt_config->setValue("theme", UISettings::values.theme); qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence); diff --git a/src/yuzu/configuration/configure_audio.h b/src/yuzu/configuration/configure_audio.h index 207f9dfb3..8771421c0 100644 --- a/src/yuzu/configuration/configure_audio.h +++ b/src/yuzu/configuration/configure_audio.h @@ -16,15 +16,14 @@ class ConfigureAudio : public QWidget { public: explicit ConfigureAudio(QWidget* parent = nullptr); - ~ConfigureAudio(); + ~ConfigureAudio() override; void applyConfiguration(); void retranslateUi(); -public slots: +private: void updateAudioDevices(int sink_index); -private: void setConfiguration(); void setOutputSinkFromSinkID(); void setAudioDeviceFromDeviceID(); diff --git a/src/yuzu/configuration/configure_debug.h b/src/yuzu/configuration/configure_debug.h index d167eb996..c6420b18c 100644 --- a/src/yuzu/configuration/configure_debug.h +++ b/src/yuzu/configuration/configure_debug.h @@ -16,13 +16,12 @@ class ConfigureDebug : public QWidget { public: explicit ConfigureDebug(QWidget* parent = nullptr); - ~ConfigureDebug(); + ~ConfigureDebug() override; void applyConfiguration(); private: void setConfiguration(); -private: std::unique_ptr<Ui::ConfigureDebug> ui; }; diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h index bbbdacc29..f6df7b827 100644 --- a/src/yuzu/configuration/configure_dialog.h +++ b/src/yuzu/configuration/configure_dialog.h @@ -18,13 +18,12 @@ class ConfigureDialog : public QDialog { public: explicit ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry); - ~ConfigureDialog(); + ~ConfigureDialog() override; void applyConfiguration(); private: void setConfiguration(); -private: std::unique_ptr<Ui::ConfigureDialog> ui; }; diff --git a/src/yuzu/configuration/configure_gamelist.h b/src/yuzu/configuration/configure_gamelist.h index bbf7e25f1..bf3f1cdfa 100644 --- a/src/yuzu/configuration/configure_gamelist.h +++ b/src/yuzu/configuration/configure_gamelist.h @@ -16,7 +16,7 @@ class ConfigureGameList : public QWidget { public: explicit ConfigureGameList(QWidget* parent = nullptr); - ~ConfigureGameList(); + ~ConfigureGameList() override; void applyConfiguration(); diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h index 4770034cc..59738af40 100644 --- a/src/yuzu/configuration/configure_general.h +++ b/src/yuzu/configuration/configure_general.h @@ -18,7 +18,7 @@ class ConfigureGeneral : public QWidget { public: explicit ConfigureGeneral(QWidget* parent = nullptr); - ~ConfigureGeneral(); + ~ConfigureGeneral() override; void PopulateHotkeyList(const HotkeyRegistry& registry); void applyConfiguration(); diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index 9bda26fd6..d6ffc6fde 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h @@ -16,14 +16,13 @@ class ConfigureGraphics : public QWidget { public: explicit ConfigureGraphics(QWidget* parent = nullptr); - ~ConfigureGraphics(); + ~ConfigureGraphics() override; void applyConfiguration(); private: void setConfiguration(); -private: std::unique_ptr<Ui::ConfigureGraphics> ui; QColor bg_color; }; diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 0527d098c..830d26115 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -88,6 +88,8 @@ ConfigureInput::ConfigureInput(QWidget* parent) [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); }); } +ConfigureInput::~ConfigureInput() = default; + void ConfigureInput::OnDockedModeChanged(bool last_state, bool new_state) { if (ui->use_docked_mode->isChecked() && ui->handheld_connected->isChecked()) { ui->handheld_connected->setChecked(false); diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h index e8723dfcb..1649e4c0b 100644 --- a/src/yuzu/configuration/configure_input.h +++ b/src/yuzu/configuration/configure_input.h @@ -25,6 +25,7 @@ class ConfigureInput : public QWidget { public: explicit ConfigureInput(QWidget* parent = nullptr); + ~ConfigureInput() override; /// Save all button configurations to settings file void applyConfiguration(); diff --git a/src/yuzu/configuration/configure_per_general.cpp b/src/yuzu/configuration/configure_per_general.cpp new file mode 100644 index 000000000..80109b434 --- /dev/null +++ b/src/yuzu/configuration/configure_per_general.cpp @@ -0,0 +1,170 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <memory> +#include <utility> + +#include <QHeaderView> +#include <QMenu> +#include <QMessageBox> +#include <QStandardItemModel> +#include <QString> +#include <QTimer> +#include <QTreeView> + +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/patch_manager.h" +#include "core/file_sys/xts_archive.h" +#include "core/loader/loader.h" +#include "ui_configure_per_general.h" +#include "yuzu/configuration/config.h" +#include "yuzu/configuration/configure_input.h" +#include "yuzu/configuration/configure_per_general.h" +#include "yuzu/ui_settings.h" +#include "yuzu/util/util.h" + +ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id) + : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGameGeneral>()), title_id(title_id) { + + ui->setupUi(this); + setFocusPolicy(Qt::ClickFocus); + setWindowTitle(tr("Properties")); + + layout = new QVBoxLayout; + tree_view = new QTreeView; + item_model = new QStandardItemModel(tree_view); + tree_view->setModel(item_model); + tree_view->setAlternatingRowColors(true); + tree_view->setSelectionMode(QHeaderView::SingleSelection); + tree_view->setSelectionBehavior(QHeaderView::SelectRows); + tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel); + tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel); + tree_view->setSortingEnabled(true); + tree_view->setEditTriggers(QHeaderView::NoEditTriggers); + tree_view->setUniformRowHeights(true); + tree_view->setContextMenuPolicy(Qt::NoContextMenu); + + item_model->insertColumns(0, 2); + item_model->setHeaderData(0, Qt::Horizontal, "Patch Name"); + item_model->setHeaderData(1, Qt::Horizontal, "Version"); + + // We must register all custom types with the Qt Automoc system so that we are able to use it + // with signals/slots. In this case, QList falls under the umbrells of custom types. + qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); + + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + layout->addWidget(tree_view); + + ui->scrollArea->setLayout(layout); + + scene = new QGraphicsScene; + ui->icon_view->setScene(scene); + + connect(item_model, &QStandardItemModel::itemChanged, + [] { UISettings::values.is_game_list_reload_pending.exchange(true); }); + + this->loadConfiguration(); +} + +ConfigurePerGameGeneral::~ConfigurePerGameGeneral() = default; + +void ConfigurePerGameGeneral::applyConfiguration() { + std::vector<std::string> disabled_addons; + + for (const auto& item : list_items) { + const auto disabled = item.front()->checkState() == Qt::Unchecked; + if (disabled) + disabled_addons.push_back(item.front()->text().toStdString()); + } + + Settings::values.disabled_addons[title_id] = disabled_addons; +} + +void ConfigurePerGameGeneral::loadFromFile(FileSys::VirtualFile file) { + this->file = std::move(file); + this->loadConfiguration(); +} + +void ConfigurePerGameGeneral::loadConfiguration() { + if (file == nullptr) + return; + + const auto loader = Loader::GetLoader(file); + + ui->display_title_id->setText(fmt::format("{:016X}", title_id).c_str()); + + FileSys::PatchManager pm{title_id}; + const auto control = pm.GetControlMetadata(); + + if (control.first != nullptr) { + ui->display_version->setText(QString::fromStdString(control.first->GetVersionString())); + ui->display_name->setText(QString::fromStdString(control.first->GetApplicationName())); + ui->display_developer->setText(QString::fromStdString(control.first->GetDeveloperName())); + } else { + std::string title; + if (loader->ReadTitle(title) == Loader::ResultStatus::Success) + ui->display_name->setText(QString::fromStdString(title)); + + std::string developer; + if (loader->ReadDeveloper(developer) == Loader::ResultStatus::Success) + ui->display_developer->setText(QString::fromStdString(developer)); + + ui->display_version->setText(QStringLiteral("1.0.0")); + } + + if (control.second != nullptr) { + scene->clear(); + + QPixmap map; + const auto bytes = control.second->ReadAllBytes(); + map.loadFromData(bytes.data(), bytes.size()); + + scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(), + Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + } else { + std::vector<u8> bytes; + if (loader->ReadIcon(bytes) == Loader::ResultStatus::Success) { + scene->clear(); + + QPixmap map; + map.loadFromData(bytes.data(), bytes.size()); + + scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(), + Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + } + } + + FileSys::VirtualFile update_raw; + loader->ReadUpdateRaw(update_raw); + + const auto& disabled = Settings::values.disabled_addons[title_id]; + + for (const auto& patch : pm.GetPatchVersionNames(update_raw)) { + QStandardItem* first_item = new QStandardItem; + const auto name = QString::fromStdString(patch.first).replace("[D] ", ""); + first_item->setText(name); + first_item->setCheckable(true); + + const auto patch_disabled = + std::find(disabled.begin(), disabled.end(), name.toStdString()) != disabled.end(); + + first_item->setCheckState(patch_disabled ? Qt::Unchecked : Qt::Checked); + + list_items.push_back(QList<QStandardItem*>{ + first_item, new QStandardItem{QString::fromStdString(patch.second)}}); + item_model->appendRow(list_items.back()); + } + + tree_view->setColumnWidth(0, 5 * tree_view->width() / 16); + + ui->display_filename->setText(QString::fromStdString(file->GetName())); + + ui->display_format->setText( + QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))); + + const auto valueText = ReadableByteSize(file->GetSize()); + ui->display_size->setText(valueText); +} diff --git a/src/yuzu/configuration/configure_per_general.h b/src/yuzu/configuration/configure_per_general.h new file mode 100644 index 000000000..a4494446c --- /dev/null +++ b/src/yuzu/configuration/configure_per_general.h @@ -0,0 +1,50 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <vector> + +#include <QKeyEvent> +#include <QList> +#include <QWidget> + +#include "core/file_sys/vfs_types.h" + +class QTreeView; +class QGraphicsScene; +class QStandardItem; +class QStandardItemModel; + +namespace Ui { +class ConfigurePerGameGeneral; +} + +class ConfigurePerGameGeneral : public QDialog { + Q_OBJECT + +public: + explicit ConfigurePerGameGeneral(QWidget* parent, u64 title_id); + ~ConfigurePerGameGeneral() override; + + /// Save all button configurations to settings file + void applyConfiguration(); + + void loadFromFile(FileSys::VirtualFile file); + +private: + std::unique_ptr<Ui::ConfigurePerGameGeneral> ui; + FileSys::VirtualFile file; + u64 title_id; + + QVBoxLayout* layout; + QTreeView* tree_view; + QStandardItemModel* item_model; + QGraphicsScene* scene; + + std::vector<QList<QStandardItem*>> list_items; + + void loadConfiguration(); +}; diff --git a/src/yuzu/configuration/configure_per_general.ui b/src/yuzu/configuration/configure_per_general.ui new file mode 100644 index 000000000..8fdd96fa4 --- /dev/null +++ b/src/yuzu/configuration/configure_per_general.ui @@ -0,0 +1,276 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigurePerGameGeneral</class> + <widget class="QDialog" name="ConfigurePerGameGeneral"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>520</height> + </rect> + </property> + <property name="windowTitle"> + <string>ConfigurePerGameGeneral</string> + </property> + <layout class="QHBoxLayout" name="HorizontalLayout"> + <item> + <layout class="QVBoxLayout" name="VerticalLayout"> + <item> + <widget class="QGroupBox" name="GeneralGroupBox"> + <property name="title"> + <string>Info</string> + </property> + <layout class="QHBoxLayout" name="GeneralHorizontalLayout"> + <item> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="6" column="1" colspan="2"> + <widget class="QLineEdit" name="display_filename"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="display_name"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Developer</string> + </property> + </widget> + </item> + <item row="5" column="1" colspan="2"> + <widget class="QLineEdit" name="display_size"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Name</string> + </property> + </widget> + </item> + <item row="6" column="0"> + <widget class="QLabel" name="label_7"> + <property name="text"> + <string>Filename</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Version</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Format</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="display_version"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QLineEdit" name="display_format"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="label_6"> + <property name="text"> + <string>Size</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="display_developer"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Title ID</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLineEdit" name="display_title_id"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="2" rowspan="5"> + <widget class="QGraphicsView" name="icon_view"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>128</width> + <height>128</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>128</width> + <height>128</height> + </size> + </property> + <property name="verticalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> + </property> + <property name="horizontalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> + </property> + <property name="sizeAdjustPolicy"> + <enum>QAbstractScrollArea::AdjustToContents</enum> + </property> + <property name="interactive"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="PerformanceGroupBox"> + <property name="title"> + <string>Add-Ons</string> + </property> + <layout class="QHBoxLayout" name="PerformanceHorizontalLayout"> + <item> + <widget class="QScrollArea" name="scrollArea"> + <property name="widgetResizable"> + <bool>true</bool> + </property> + <widget class="QWidget" name="scrollAreaWidgetContents"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>350</width> + <height>169</height> + </rect> + </property> + </widget> + </widget> + </item> + <item> + <layout class="QVBoxLayout" name="PerformanceVerticalLayout"/> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>ConfigurePerGameGeneral</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>269</x> + <y>567</y> + </hint> + <hint type="destinationlabel"> + <x>269</x> + <y>294</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>ConfigurePerGameGeneral</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>269</x> + <y>567</y> + </hint> + <hint type="destinationlabel"> + <x>269</x> + <y>294</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/yuzu/configuration/configure_web.h b/src/yuzu/configuration/configure_web.h index 7741ab95d..7752ae4a1 100644 --- a/src/yuzu/configuration/configure_web.h +++ b/src/yuzu/configuration/configure_web.h @@ -17,18 +17,17 @@ class ConfigureWeb : public QWidget { public: explicit ConfigureWeb(QWidget* parent = nullptr); - ~ConfigureWeb(); + ~ConfigureWeb() override; void applyConfiguration(); void retranslateUi(); -public slots: +private: void RefreshTelemetryID(); void OnLoginChanged(); void VerifyLogin(); void OnLoginVerified(); -private: void setConfiguration(); bool user_verified = true; diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 0c831c9f4..f9c18ede4 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -7,10 +7,10 @@ #include "common/assert.h" #include "core/core.h" -#include "core/hle/kernel/event.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/process.h" +#include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/timer.h" @@ -153,8 +153,8 @@ QString WaitTreeWaitObject::GetText() const { std::unique_ptr<WaitTreeWaitObject> WaitTreeWaitObject::make(const Kernel::WaitObject& object) { switch (object.GetHandleType()) { - case Kernel::HandleType::Event: - return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::Event&>(object)); + case Kernel::HandleType::ReadableEvent: + return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::ReadableEvent&>(object)); case Kernel::HandleType::Timer: return std::make_unique<WaitTreeTimer>(static_cast<const Kernel::Timer&>(object)); case Kernel::HandleType::Thread: @@ -332,7 +332,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { return list; } -WaitTreeEvent::WaitTreeEvent(const Kernel::Event& object) : WaitTreeWaitObject(object) {} +WaitTreeEvent::WaitTreeEvent(const Kernel::ReadableEvent& object) : WaitTreeWaitObject(object) {} WaitTreeEvent::~WaitTreeEvent() = default; std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeEvent::GetChildren() const { @@ -340,7 +340,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeEvent::GetChildren() const { list.push_back(std::make_unique<WaitTreeText>( tr("reset type = %1") - .arg(GetResetTypeQString(static_cast<const Kernel::Event&>(object).GetResetType())))); + .arg(GetResetTypeQString( + static_cast<const Kernel::ReadableEvent&>(object).GetResetType())))); return list; } diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h index 331f89885..492fb6ac9 100644 --- a/src/yuzu/debugger/wait_tree.h +++ b/src/yuzu/debugger/wait_tree.h @@ -17,8 +17,8 @@ class EmuThread; namespace Kernel { +class ReadableEvent; class WaitObject; -class Event; class Thread; class Timer; } // namespace Kernel @@ -144,7 +144,7 @@ public: class WaitTreeEvent : public WaitTreeWaitObject { Q_OBJECT public: - explicit WaitTreeEvent(const Kernel::Event& object); + explicit WaitTreeEvent(const Kernel::ReadableEvent& object); ~WaitTreeEvent() override; std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index b52a50915..8e9524fd6 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -333,6 +333,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); + context_menu.addSeparator(); + QAction* properties = context_menu.addAction(tr("Properties")); open_save_location->setEnabled(program_id != 0); auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); @@ -346,6 +348,7 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); }); connect(navigate_to_gamedb_entry, &QAction::triggered, [&]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); }); + connect(properties, &QAction::triggered, [&]() { emit OpenPerGameGeneralRequested(path); }); context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location)); } diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 05e115e19..b317eb2fc 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -70,6 +70,7 @@ signals: void CopyTIDRequested(u64 program_id); void NavigateToGamedbEntryRequested(u64 program_id, const CompatibilityList& compatibility_list); + void OpenPerGameGeneralRequested(const std::string& file); private slots: void onTextChanged(const QString& newText); diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 362902e46..b37710f59 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -62,7 +62,7 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager, FileSys::VirtualFile update_raw; loader.ReadUpdateRaw(update_raw); for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) { - const bool is_update = kv.first == "Update"; + const bool is_update = kv.first == "Update" || kv.first == "[D] Update"; if (!updatable && is_update) { continue; } @@ -86,6 +86,37 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager, out.chop(1); return out; } + +QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::string& name, + const std::vector<u8>& icon, Loader::AppLoader& loader, + u64 program_id, const CompatibilityList& compatibility_list, + const FileSys::PatchManager& patch) { + const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); + + // The game list uses this as compatibility number for untested games + QString compatibility{"99"}; + if (it != compatibility_list.end()) { + compatibility = it->second.first; + } + + const auto file_type = loader.GetFileType(); + const auto file_type_string = QString::fromStdString(Loader::GetFileTypeString(file_type)); + + QList<QStandardItem*> list{ + new GameListItemPath(FormatGameName(path), icon, QString::fromStdString(name), + file_type_string, program_id), + new GameListItemCompat(compatibility), + new GameListItem(file_type_string), + new GameListItemSize(FileUtil::GetSize(path)), + }; + + if (UISettings::values.show_add_ons) { + list.insert( + 2, new GameListItem(FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable()))); + } + + return list; +} } // Anonymous namespace GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan, @@ -97,11 +128,11 @@ GameListWorker::~GameListWorker() = default; void GameListWorker::AddInstalledTitlesToGameList() { const auto cache = Service::FileSystem::GetUnionContents(); - const auto installed_games = cache->ListEntriesFilter(FileSys::TitleType::Application, - FileSys::ContentRecordType::Program); + const auto installed_games = cache.ListEntriesFilter(FileSys::TitleType::Application, + FileSys::ContentRecordType::Program); for (const auto& game : installed_games) { - const auto file = cache->GetEntryUnparsed(game); + const auto file = cache.GetEntryUnparsed(game); std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file); if (!loader) continue; @@ -112,40 +143,19 @@ void GameListWorker::AddInstalledTitlesToGameList() { loader->ReadProgramId(program_id); const FileSys::PatchManager patch{program_id}; - const auto control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control); + const auto control = cache.GetEntry(game.title_id, FileSys::ContentRecordType::Control); if (control != nullptr) GetMetadataFromControlNCA(patch, *control, icon, name); - auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); - - // The game list uses this as compatibility number for untested games - QString compatibility("99"); - if (it != compatibility_list.end()) - compatibility = it->second.first; - - QList<QStandardItem*> list{ - new GameListItemPath( - FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name), - QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), - program_id), - new GameListItemCompat(compatibility), - new GameListItem( - QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), - new GameListItemSize(file->GetSize()), - }; - - if (UISettings::values.show_add_ons) { - list.insert(2, new GameListItem(FormatPatchNameVersions(patch, *loader))); - } - - emit EntryReady(list); + emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id, + compatibility_list, patch)); } - const auto control_data = cache->ListEntriesFilter(FileSys::TitleType::Application, - FileSys::ContentRecordType::Control); + const auto control_data = cache.ListEntriesFilter(FileSys::TitleType::Application, + FileSys::ContentRecordType::Control); for (const auto& entry : control_data) { - auto nca = cache->GetEntry(entry); + auto nca = cache.GetEntry(entry); if (nca != nullptr) { nca_control_map.insert_or_assign(entry.title_id, std::move(nca)); } @@ -155,14 +165,14 @@ void GameListWorker::AddInstalledTitlesToGameList() { void GameListWorker::FillControlMap(const std::string& dir_path) { const auto nca_control_callback = [this](u64* num_entries_out, const std::string& directory, const std::string& virtual_name) -> bool { - std::string physical_name = directory + DIR_SEP + virtual_name; - - if (stop_processing) - return false; // Breaks the callback loop. + if (stop_processing) { + // Breaks the callback loop + return false; + } - bool is_dir = FileUtil::IsDirectory(physical_name); - QFileInfo file_info(physical_name.c_str()); - if (!is_dir && file_info.suffix().toStdString() == "nca") { + const std::string physical_name = directory + DIR_SEP + virtual_name; + const QFileInfo file_info(QString::fromStdString(physical_name)); + if (!file_info.isDir() && file_info.suffix() == QStringLiteral("nca")) { auto nca = std::make_unique<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read)); if (nca->GetType() == FileSys::NCAContentType::Control) { @@ -179,20 +189,25 @@ void GameListWorker::FillControlMap(const std::string& dir_path) { void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) { const auto callback = [this, recursion](u64* num_entries_out, const std::string& directory, const std::string& virtual_name) -> bool { - std::string physical_name = directory + DIR_SEP + virtual_name; - - if (stop_processing) - return false; // Breaks the callback loop. + if (stop_processing) { + // Breaks the callback loop. + return false; + } - bool is_dir = FileUtil::IsDirectory(physical_name); + const std::string physical_name = directory + DIR_SEP + virtual_name; + const bool is_dir = FileUtil::IsDirectory(physical_name); if (!is_dir && (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { - std::unique_ptr<Loader::AppLoader> loader = - Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read)); - if (!loader || ((loader->GetFileType() == Loader::FileType::Unknown || - loader->GetFileType() == Loader::FileType::Error) && - !UISettings::values.show_unknown)) + auto loader = Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read)); + if (!loader) { return true; + } + + const auto file_type = loader->GetFileType(); + if ((file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) && + !UISettings::values.show_unknown) { + return true; + } std::vector<u8> icon; const auto res1 = loader->ReadIcon(icon); @@ -214,30 +229,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign } } - auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); - - // The game list uses this as compatibility number for untested games - QString compatibility("99"); - if (it != compatibility_list.end()) - compatibility = it->second.first; - - QList<QStandardItem*> list{ - new GameListItemPath( - FormatGameName(physical_name), icon, QString::fromStdString(name), - QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), - program_id), - new GameListItemCompat(compatibility), - new GameListItem( - QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), - new GameListItemSize(FileUtil::GetSize(physical_name)), - }; - - if (UISettings::values.show_add_ons) { - list.insert(2, new GameListItem(FormatPatchNameVersions( - patch, *loader, loader->IsRomFSUpdatable()))); - } - - emit EntryReady(std::move(list)); + emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id, + compatibility_list, patch)); } else if (is_dir && recursion > 0) { watch_list.append(QString::fromStdString(physical_name)); AddFstEntriesToGameList(physical_name, recursion - 1); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index d4010001d..90b212ba5 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -9,6 +9,7 @@ // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. #include "applets/software_keyboard.h" +#include "configuration/configure_per_general.h" #include "core/file_sys/vfs.h" #include "core/file_sys/vfs_real.h" #include "core/hle/service/acc/profile_manager.h" @@ -441,6 +442,8 @@ void GMainWindow::ConnectWidgetEvents() { connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, &GMainWindow::OnGameListNavigateToGamedbEntry); + connect(game_list, &GameList::OpenPerGameGeneralRequested, this, + &GMainWindow::OnGameListOpenPerGameProperties); connect(this, &GMainWindow::EmulationStarting, render_window, &GRenderWindow::OnEmulationStarting); @@ -907,7 +910,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa } const auto installed = Service::FileSystem::GetUnionContents(); - auto romfs_title_id = SelectRomFSDumpTarget(*installed, program_id); + const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id); if (!romfs_title_id) { failed(); @@ -922,7 +925,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa if (*romfs_title_id == program_id) { romfs = file; } else { - romfs = installed->GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS(); + romfs = installed.GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS(); } const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); @@ -988,6 +991,32 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, QDesktopServices::openUrl(QUrl("https://yuzu-emu.org/game/" + directory)); } +void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { + u64 title_id{}; + const auto v_file = Core::GetGameFileFromPath(vfs, file); + const auto loader = Loader::GetLoader(v_file); + if (loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) { + QMessageBox::information(this, tr("Properties"), + tr("The game properties could not be loaded.")); + return; + } + + ConfigurePerGameGeneral dialog(this, title_id); + dialog.loadFromFile(v_file); + auto result = dialog.exec(); + if (result == QDialog::Accepted) { + dialog.applyConfiguration(); + + const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); + if (reload) { + game_list->PopulateAsync(UISettings::values.gamedir, + UISettings::values.gamedir_deepscan); + } + + config->Save(); + } +} + void GMainWindow::OnMenuLoadFile() { const QString extensions = QString("*.").append(GameList::supported_file_extensions.join(" *.")).append(" main"); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 674e73412..ca9c50367 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -168,6 +168,7 @@ private slots: void OnGameListCopyTID(u64 program_id); void OnGameListNavigateToGamedbEntry(u64 program_id, const CompatibilityList& compatibility_list); + void OnGameListOpenPerGameProperties(const std::string& file); void OnMenuLoadFile(); void OnMenuLoadFolder(); void OnMenuInstallToNAND(); diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 097c1fbe3..fe0d1eebf 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <memory> +#include <sstream> #include <SDL.h> #include <inih/cpp/INIReader.h> #include "common/file_util.h" @@ -369,6 +370,23 @@ void Config::ReadValues() { Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false); Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); + const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); + std::stringstream ss(title_list); + std::string line; + while (std::getline(ss, line, '|')) { + const auto title_id = std::stoul(line, nullptr, 16); + const auto disabled_list = sdl2_config->Get("AddOns", "disabled_" + line, ""); + + std::stringstream inner_ss(disabled_list); + std::string inner_line; + std::vector<std::string> out; + while (std::getline(inner_ss, inner_line, '|')) { + out.push_back(inner_line); + } + + Settings::values.disabled_addons.insert_or_assign(title_id, out); + } + // Web Service Settings::values.enable_telemetry = sdl2_config->GetBoolean("WebService", "enable_telemetry", true); diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index d73669f36..0f3f8da50 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -221,5 +221,12 @@ web_api_url = https://api.yuzu-emu.org # See https://profile.yuzu-emu.org/ for more info yuzu_username = yuzu_token = + +[AddOns] +# Used to disable add-ons +# List of title IDs of games that will have add-ons disabled (separated by '|'): +title_ids = +# For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|') +# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey )"; } |