diff options
| author | 2018-12-10 21:52:19 -0500 | |
|---|---|---|
| committer | 2018-12-10 21:52:19 -0500 | |
| commit | 2c45c6d23431021efb1053abcb40064256aac338 (patch) | |
| tree | edd00a30fa32460e8158992d53768038e83141dc /src | |
| parent | Merge pull request #1887 from FearlessTobi/port-4476 (diff) | |
| parent | qt: Add Properties menu to game list right-click (diff) | |
| download | yuzu-2c45c6d23431021efb1053abcb40064256aac338.tar.gz yuzu-2c45c6d23431021efb1053abcb40064256aac338.tar.xz yuzu-2c45c6d23431021efb1053abcb40064256aac338.zip | |
Merge pull request #1819 from DarkLordZach/disable-addons
patch_manager: Add support for disabling patches
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/core.cpp | 8 | ||||
| -rw-r--r-- | src/core/core.h | 4 | ||||
| -rw-r--r-- | src/core/file_sys/patch_manager.cpp | 56 | ||||
| -rw-r--r-- | src/core/file_sys/patch_manager.h | 5 | ||||
| -rw-r--r-- | src/core/hle/service/aoc/aoc_u.cpp | 12 | ||||
| -rw-r--r-- | src/core/loader/loader.h | 10 | ||||
| -rw-r--r-- | src/core/loader/nsp.cpp | 7 | ||||
| -rw-r--r-- | src/core/loader/nsp.h | 1 | ||||
| -rw-r--r-- | src/core/loader/xci.cpp | 7 | ||||
| -rw-r--r-- | src/core/loader/xci.h | 1 | ||||
| -rw-r--r-- | src/core/settings.h | 5 | ||||
| -rw-r--r-- | src/yuzu/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/yuzu/configuration/config.cpp | 30 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_per_general.cpp | 170 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_per_general.h | 50 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_per_general.ui | 276 | ||||
| -rw-r--r-- | src/yuzu/game_list.cpp | 3 | ||||
| -rw-r--r-- | src/yuzu/game_list.h | 1 | ||||
| -rw-r--r-- | src/yuzu/game_list_worker.cpp | 2 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 29 | ||||
| -rw-r--r-- | src/yuzu/main.h | 1 | ||||
| -rw-r--r-- | src/yuzu_cmd/config.cpp | 18 | ||||
| -rw-r--r-- | src/yuzu_cmd/default_ini.h | 7 |
23 files changed, 691 insertions, 15 deletions
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 @@ | |||
| 8 | #include <thread> | 8 | #include <thread> |
| 9 | #include <utility> | 9 | #include <utility> |
| 10 | 10 | ||
| 11 | #include "common/file_util.h" | ||
| 11 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 12 | #include "common/string_util.h" | 13 | #include "common/string_util.h" |
| 13 | #include "core/arm/exclusive_monitor.h" | 14 | #include "core/arm/exclusive_monitor.h" |
| @@ -40,7 +41,6 @@ namespace Core { | |||
| 40 | 41 | ||
| 41 | /*static*/ System System::s_instance; | 42 | /*static*/ System System::s_instance; |
| 42 | 43 | ||
| 43 | namespace { | ||
| 44 | FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | 44 | FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, |
| 45 | const std::string& path) { | 45 | const std::string& path) { |
| 46 | // To account for split 00+01+etc files. | 46 | // To account for split 00+01+etc files. |
| @@ -69,11 +69,13 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | |||
| 69 | return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName()); | 69 | return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName()); |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | if (FileUtil::IsDirectory(path)) | ||
| 73 | return vfs->OpenFile(path + "/" + "main", FileSys::Mode::Read); | ||
| 74 | |||
| 72 | return vfs->OpenFile(path, FileSys::Mode::Read); | 75 | return vfs->OpenFile(path, FileSys::Mode::Read); |
| 73 | } | 76 | } |
| 74 | } // Anonymous namespace | ||
| 75 | |||
| 76 | struct System::Impl { | 77 | struct System::Impl { |
| 78 | |||
| 77 | Cpu& CurrentCpuCore() { | 79 | Cpu& CurrentCpuCore() { |
| 78 | return cpu_core_manager.GetCurrentCore(); | 80 | return cpu_core_manager.GetCurrentCore(); |
| 79 | } | 81 | } |
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 @@ | |||
| 9 | #include <string> | 9 | #include <string> |
| 10 | 10 | ||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "core/file_sys/vfs_types.h" | ||
| 12 | #include "core/hle/kernel/object.h" | 13 | #include "core/hle/kernel/object.h" |
| 13 | 14 | ||
| 14 | namespace Core::Frontend { | 15 | namespace Core::Frontend { |
| @@ -55,6 +56,9 @@ class TelemetrySession; | |||
| 55 | 56 | ||
| 56 | struct PerfStatsResults; | 57 | struct PerfStatsResults; |
| 57 | 58 | ||
| 59 | FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | ||
| 60 | const std::string& path); | ||
| 61 | |||
| 58 | class System { | 62 | class System { |
| 59 | public: | 63 | public: |
| 60 | System(const System&) = delete; | 64 | System(const System&) = delete; |
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 6b14e08be..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) {} | |||
| 56 | 56 | ||
| 57 | PatchManager::~PatchManager() = default; | 57 | PatchManager::~PatchManager() = default; |
| 58 | 58 | ||
| 59 | u64 PatchManager::GetTitleID() const { | ||
| 60 | return title_id; | ||
| 61 | } | ||
| 62 | |||
| 59 | VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | 63 | VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { |
| 60 | LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id); | 64 | LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id); |
| 61 | 65 | ||
| @@ -73,11 +77,15 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | |||
| 73 | 77 | ||
| 74 | const auto installed = Service::FileSystem::GetUnionContents(); | 78 | const auto installed = Service::FileSystem::GetUnionContents(); |
| 75 | 79 | ||
| 80 | const auto& disabled = Settings::values.disabled_addons[title_id]; | ||
| 81 | const auto update_disabled = | ||
| 82 | std::find(disabled.begin(), disabled.end(), "Update") != disabled.end(); | ||
| 83 | |||
| 76 | // Game Updates | 84 | // Game Updates |
| 77 | const auto update_tid = GetUpdateTitleID(title_id); | 85 | const auto update_tid = GetUpdateTitleID(title_id); |
| 78 | const auto update = installed.GetEntry(update_tid, ContentRecordType::Program); | 86 | const auto update = installed.GetEntry(update_tid, ContentRecordType::Program); |
| 79 | 87 | ||
| 80 | if (update != nullptr && update->GetExeFS() != nullptr && | 88 | if (!update_disabled && update != nullptr && update->GetExeFS() != nullptr && |
| 81 | update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { | 89 | update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { |
| 82 | LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully", | 90 | LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully", |
| 83 | FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0))); | 91 | FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0))); |
| @@ -95,6 +103,9 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | |||
| 95 | std::vector<VirtualDir> layers; | 103 | std::vector<VirtualDir> layers; |
| 96 | layers.reserve(patch_dirs.size() + 1); | 104 | layers.reserve(patch_dirs.size() + 1); |
| 97 | for (const auto& subdir : patch_dirs) { | 105 | for (const auto& subdir : patch_dirs) { |
| 106 | if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end()) | ||
| 107 | continue; | ||
| 108 | |||
| 98 | auto exefs_dir = subdir->GetSubdirectory("exefs"); | 109 | auto exefs_dir = subdir->GetSubdirectory("exefs"); |
| 99 | if (exefs_dir != nullptr) | 110 | if (exefs_dir != nullptr) |
| 100 | layers.push_back(std::move(exefs_dir)); | 111 | layers.push_back(std::move(exefs_dir)); |
| @@ -111,11 +122,16 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | |||
| 111 | return exefs; | 122 | return exefs; |
| 112 | } | 123 | } |
| 113 | 124 | ||
| 114 | static std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs, | 125 | std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, |
| 115 | const std::string& build_id) { | 126 | const std::string& build_id) const { |
| 127 | const auto& disabled = Settings::values.disabled_addons[title_id]; | ||
| 128 | |||
| 116 | std::vector<VirtualFile> out; | 129 | std::vector<VirtualFile> out; |
| 117 | out.reserve(patch_dirs.size()); | 130 | out.reserve(patch_dirs.size()); |
| 118 | for (const auto& subdir : patch_dirs) { | 131 | for (const auto& subdir : patch_dirs) { |
| 132 | if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end()) | ||
| 133 | continue; | ||
| 134 | |||
| 119 | auto exefs_dir = subdir->GetSubdirectory("exefs"); | 135 | auto exefs_dir = subdir->GetSubdirectory("exefs"); |
| 120 | if (exefs_dir != nullptr) { | 136 | if (exefs_dir != nullptr) { |
| 121 | for (const auto& file : exefs_dir->GetFiles()) { | 137 | for (const auto& file : exefs_dir->GetFiles()) { |
| @@ -228,6 +244,7 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t | |||
| 228 | return; | 244 | return; |
| 229 | } | 245 | } |
| 230 | 246 | ||
| 247 | const auto& disabled = Settings::values.disabled_addons[title_id]; | ||
| 231 | auto patch_dirs = load_dir->GetSubdirectories(); | 248 | auto patch_dirs = load_dir->GetSubdirectories(); |
| 232 | std::sort(patch_dirs.begin(), patch_dirs.end(), | 249 | std::sort(patch_dirs.begin(), patch_dirs.end(), |
| 233 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | 250 | [](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 | |||
| 237 | layers.reserve(patch_dirs.size() + 1); | 254 | layers.reserve(patch_dirs.size() + 1); |
| 238 | layers_ext.reserve(patch_dirs.size() + 1); | 255 | layers_ext.reserve(patch_dirs.size() + 1); |
| 239 | for (const auto& subdir : patch_dirs) { | 256 | for (const auto& subdir : patch_dirs) { |
| 257 | if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end()) | ||
| 258 | continue; | ||
| 259 | |||
| 240 | auto romfs_dir = subdir->GetSubdirectory("romfs"); | 260 | auto romfs_dir = subdir->GetSubdirectory("romfs"); |
| 241 | if (romfs_dir != nullptr) | 261 | if (romfs_dir != nullptr) |
| 242 | layers.push_back(std::move(romfs_dir)); | 262 | layers.push_back(std::move(romfs_dir)); |
| @@ -282,7 +302,12 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content | |||
| 282 | // Game Updates | 302 | // Game Updates |
| 283 | const auto update_tid = GetUpdateTitleID(title_id); | 303 | const auto update_tid = GetUpdateTitleID(title_id); |
| 284 | const auto update = installed.GetEntryRaw(update_tid, type); | 304 | const auto update = installed.GetEntryRaw(update_tid, type); |
| 285 | if (update != nullptr) { | 305 | |
| 306 | const auto& disabled = Settings::values.disabled_addons[title_id]; | ||
| 307 | const auto update_disabled = | ||
| 308 | std::find(disabled.begin(), disabled.end(), "Update") != disabled.end(); | ||
| 309 | |||
| 310 | if (!update_disabled && update != nullptr) { | ||
| 286 | const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset); | 311 | const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset); |
| 287 | if (new_nca->GetStatus() == Loader::ResultStatus::Success && | 312 | if (new_nca->GetStatus() == Loader::ResultStatus::Success && |
| 288 | new_nca->GetRomFS() != nullptr) { | 313 | new_nca->GetRomFS() != nullptr) { |
| @@ -290,7 +315,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content | |||
| 290 | FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0))); | 315 | FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0))); |
| 291 | romfs = new_nca->GetRomFS(); | 316 | romfs = new_nca->GetRomFS(); |
| 292 | } | 317 | } |
| 293 | } else if (update_raw != nullptr) { | 318 | } else if (!update_disabled && update_raw != nullptr) { |
| 294 | const auto new_nca = std::make_shared<NCA>(update_raw, romfs, ivfc_offset); | 319 | const auto new_nca = std::make_shared<NCA>(update_raw, romfs, ivfc_offset); |
| 295 | if (new_nca->GetStatus() == Loader::ResultStatus::Success && | 320 | if (new_nca->GetStatus() == Loader::ResultStatus::Success && |
| 296 | new_nca->GetRomFS() != nullptr) { | 321 | new_nca->GetRomFS() != nullptr) { |
| @@ -320,25 +345,30 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam | |||
| 320 | VirtualFile update_raw) const { | 345 | VirtualFile update_raw) const { |
| 321 | std::map<std::string, std::string, std::less<>> out; | 346 | std::map<std::string, std::string, std::less<>> out; |
| 322 | const auto installed = Service::FileSystem::GetUnionContents(); | 347 | const auto installed = Service::FileSystem::GetUnionContents(); |
| 348 | const auto& disabled = Settings::values.disabled_addons[title_id]; | ||
| 323 | 349 | ||
| 324 | // Game Updates | 350 | // Game Updates |
| 325 | const auto update_tid = GetUpdateTitleID(title_id); | 351 | const auto update_tid = GetUpdateTitleID(title_id); |
| 326 | PatchManager update{update_tid}; | 352 | PatchManager update{update_tid}; |
| 327 | auto [nacp, discard_icon_file] = update.GetControlMetadata(); | 353 | auto [nacp, discard_icon_file] = update.GetControlMetadata(); |
| 328 | 354 | ||
| 355 | const auto update_disabled = | ||
| 356 | std::find(disabled.begin(), disabled.end(), "Update") != disabled.end(); | ||
| 357 | const auto update_label = update_disabled ? "[D] Update" : "Update"; | ||
| 358 | |||
| 329 | if (nacp != nullptr) { | 359 | if (nacp != nullptr) { |
| 330 | out.insert_or_assign("Update", nacp->GetVersionString()); | 360 | out.insert_or_assign(update_label, nacp->GetVersionString()); |
| 331 | } else { | 361 | } else { |
| 332 | if (installed.HasEntry(update_tid, ContentRecordType::Program)) { | 362 | if (installed.HasEntry(update_tid, ContentRecordType::Program)) { |
| 333 | const auto meta_ver = installed.GetEntryVersion(update_tid); | 363 | const auto meta_ver = installed.GetEntryVersion(update_tid); |
| 334 | if (meta_ver.value_or(0) == 0) { | 364 | if (meta_ver.value_or(0) == 0) { |
| 335 | out.insert_or_assign("Update", ""); | 365 | out.insert_or_assign(update_label, ""); |
| 336 | } else { | 366 | } else { |
| 337 | out.insert_or_assign( | 367 | out.insert_or_assign( |
| 338 | "Update", FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements)); | 368 | update_label, FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements)); |
| 339 | } | 369 | } |
| 340 | } else if (update_raw != nullptr) { | 370 | } else if (update_raw != nullptr) { |
| 341 | out.insert_or_assign("Update", "PACKED"); | 371 | out.insert_or_assign(update_label, "PACKED"); |
| 342 | } | 372 | } |
| 343 | } | 373 | } |
| 344 | 374 | ||
| @@ -378,7 +408,9 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam | |||
| 378 | if (types.empty()) | 408 | if (types.empty()) |
| 379 | continue; | 409 | continue; |
| 380 | 410 | ||
| 381 | out.insert_or_assign(mod->GetName(), types); | 411 | const auto mod_disabled = |
| 412 | std::find(disabled.begin(), disabled.end(), mod->GetName()) != disabled.end(); | ||
| 413 | out.insert_or_assign(mod_disabled ? "[D] " + mod->GetName() : mod->GetName(), types); | ||
| 382 | } | 414 | } |
| 383 | } | 415 | } |
| 384 | 416 | ||
| @@ -401,7 +433,9 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam | |||
| 401 | 433 | ||
| 402 | list += fmt::format("{}", dlc_match.back().title_id & 0x7FF); | 434 | list += fmt::format("{}", dlc_match.back().title_id & 0x7FF); |
| 403 | 435 | ||
| 404 | out.insert_or_assign("DLC", std::move(list)); | 436 | const auto dlc_disabled = |
| 437 | std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end(); | ||
| 438 | out.insert_or_assign(dlc_disabled ? "[D] DLC" : "DLC", std::move(list)); | ||
| 405 | } | 439 | } |
| 406 | 440 | ||
| 407 | return out; | 441 | return out; |
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: | |||
| 30 | explicit PatchManager(u64 title_id); | 30 | explicit PatchManager(u64 title_id); |
| 31 | ~PatchManager(); | 31 | ~PatchManager(); |
| 32 | 32 | ||
| 33 | u64 GetTitleID() const; | ||
| 34 | |||
| 33 | // Currently tracked ExeFS patches: | 35 | // Currently tracked ExeFS patches: |
| 34 | // - Game Updates | 36 | // - Game Updates |
| 35 | VirtualDir PatchExeFS(VirtualDir exefs) const; | 37 | VirtualDir PatchExeFS(VirtualDir exefs) const; |
| @@ -63,6 +65,9 @@ public: | |||
| 63 | std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const; | 65 | std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const; |
| 64 | 66 | ||
| 65 | private: | 67 | private: |
| 68 | std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs, | ||
| 69 | const std::string& build_id) const; | ||
| 70 | |||
| 66 | u64 title_id; | 71 | u64 title_id; |
| 67 | }; | 72 | }; |
| 68 | 73 | ||
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 0417fdb92..b506bc3dd 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include "core/hle/service/aoc/aoc_u.h" | 20 | #include "core/hle/service/aoc/aoc_u.h" |
| 21 | #include "core/hle/service/filesystem/filesystem.h" | 21 | #include "core/hle/service/filesystem/filesystem.h" |
| 22 | #include "core/loader/loader.h" | 22 | #include "core/loader/loader.h" |
| 23 | #include "core/settings.h" | ||
| 23 | 24 | ||
| 24 | namespace Service::AOC { | 25 | namespace Service::AOC { |
| 25 | 26 | ||
| @@ -76,6 +77,13 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { | |||
| 76 | rb.Push(RESULT_SUCCESS); | 77 | rb.Push(RESULT_SUCCESS); |
| 77 | 78 | ||
| 78 | const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); | 79 | const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); |
| 80 | |||
| 81 | const auto& disabled = Settings::values.disabled_addons[current]; | ||
| 82 | if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) { | ||
| 83 | rb.Push<u32>(0); | ||
| 84 | return; | ||
| 85 | } | ||
| 86 | |||
| 79 | rb.Push<u32>(static_cast<u32>( | 87 | rb.Push<u32>(static_cast<u32>( |
| 80 | std::count_if(add_on_content.begin(), add_on_content.end(), | 88 | std::count_if(add_on_content.begin(), add_on_content.end(), |
| 81 | [current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); }))); | 89 | [current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); }))); |
| @@ -96,6 +104,10 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { | |||
| 96 | out.push_back(static_cast<u32>(add_on_content[i] & 0x7FF)); | 104 | out.push_back(static_cast<u32>(add_on_content[i] & 0x7FF)); |
| 97 | } | 105 | } |
| 98 | 106 | ||
| 107 | const auto& disabled = Settings::values.disabled_addons[current]; | ||
| 108 | if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) | ||
| 109 | out = {}; | ||
| 110 | |||
| 99 | if (out.size() < offset) { | 111 | if (out.size() < offset) { |
| 100 | IPC::ResponseBuilder rb{ctx, 2}; | 112 | IPC::ResponseBuilder rb{ctx, 2}; |
| 101 | // TODO(DarkLordZach): Find the correct error code. | 113 | // TODO(DarkLordZach): Find the correct error code. |
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 5390ab9ee..0838e303b 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include <vector> | 12 | #include <vector> |
| 13 | 13 | ||
| 14 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 15 | #include "core/file_sys/control_metadata.h" | ||
| 15 | #include "core/file_sys/vfs.h" | 16 | #include "core/file_sys/vfs.h" |
| 16 | 17 | ||
| 17 | namespace Kernel { | 18 | namespace Kernel { |
| @@ -243,6 +244,15 @@ public: | |||
| 243 | return ResultStatus::ErrorNotImplemented; | 244 | return ResultStatus::ErrorNotImplemented; |
| 244 | } | 245 | } |
| 245 | 246 | ||
| 247 | /** | ||
| 248 | * Get the developer of the application | ||
| 249 | * @param developer Reference to store the application developer into | ||
| 250 | * @return ResultStatus result of function | ||
| 251 | */ | ||
| 252 | virtual ResultStatus ReadDeveloper(std::string& developer) { | ||
| 253 | return ResultStatus::ErrorNotImplemented; | ||
| 254 | } | ||
| 255 | |||
| 246 | protected: | 256 | protected: |
| 247 | FileSys::VirtualFile file; | 257 | FileSys::VirtualFile file; |
| 248 | bool is_loaded = false; | 258 | bool is_loaded = false; |
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) { | |||
| 151 | title = nacp_file->GetApplicationName(); | 151 | title = nacp_file->GetApplicationName(); |
| 152 | return ResultStatus::Success; | 152 | return ResultStatus::Success; |
| 153 | } | 153 | } |
| 154 | |||
| 155 | ResultStatus AppLoader_NSP::ReadDeveloper(std::string& developer) { | ||
| 156 | if (nacp_file == nullptr) | ||
| 157 | return ResultStatus::ErrorNoControl; | ||
| 158 | developer = nacp_file->GetDeveloperName(); | ||
| 159 | return ResultStatus::Success; | ||
| 160 | } | ||
| 154 | } // namespace Loader | 161 | } // namespace Loader |
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h index 9ed8a59cf..2b1e0719b 100644 --- a/src/core/loader/nsp.h +++ b/src/core/loader/nsp.h | |||
| @@ -43,6 +43,7 @@ public: | |||
| 43 | ResultStatus ReadProgramId(u64& out_program_id) override; | 43 | ResultStatus ReadProgramId(u64& out_program_id) override; |
| 44 | ResultStatus ReadIcon(std::vector<u8>& buffer) override; | 44 | ResultStatus ReadIcon(std::vector<u8>& buffer) override; |
| 45 | ResultStatus ReadTitle(std::string& title) override; | 45 | ResultStatus ReadTitle(std::string& title) override; |
| 46 | ResultStatus ReadDeveloper(std::string& developer) override; | ||
| 46 | 47 | ||
| 47 | private: | 48 | private: |
| 48 | std::unique_ptr<FileSys::NSP> nsp; | 49 | 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) { | |||
| 120 | title = nacp_file->GetApplicationName(); | 120 | title = nacp_file->GetApplicationName(); |
| 121 | return ResultStatus::Success; | 121 | return ResultStatus::Success; |
| 122 | } | 122 | } |
| 123 | |||
| 124 | ResultStatus AppLoader_XCI::ReadDeveloper(std::string& developer) { | ||
| 125 | if (nacp_file == nullptr) | ||
| 126 | return ResultStatus::ErrorNoControl; | ||
| 127 | developer = nacp_file->GetDeveloperName(); | ||
| 128 | return ResultStatus::Success; | ||
| 129 | } | ||
| 123 | } // namespace Loader | 130 | } // namespace Loader |
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h index ded5bb88a..15d1b1a23 100644 --- a/src/core/loader/xci.h +++ b/src/core/loader/xci.h | |||
| @@ -43,6 +43,7 @@ public: | |||
| 43 | ResultStatus ReadProgramId(u64& out_program_id) override; | 43 | ResultStatus ReadProgramId(u64& out_program_id) override; |
| 44 | ResultStatus ReadIcon(std::vector<u8>& buffer) override; | 44 | ResultStatus ReadIcon(std::vector<u8>& buffer) override; |
| 45 | ResultStatus ReadTitle(std::string& title) override; | 45 | ResultStatus ReadTitle(std::string& title) override; |
| 46 | ResultStatus ReadDeveloper(std::string& developer) override; | ||
| 46 | 47 | ||
| 47 | private: | 48 | private: |
| 48 | std::unique_ptr<FileSys::XCI> xci; | 49 | std::unique_ptr<FileSys::XCI> xci; |
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 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <atomic> | 8 | #include <atomic> |
| 9 | #include <map> | ||
| 9 | #include <optional> | 10 | #include <optional> |
| 10 | #include <string> | 11 | #include <string> |
| 12 | #include <vector> | ||
| 11 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 12 | 14 | ||
| 13 | namespace Settings { | 15 | namespace Settings { |
| @@ -411,6 +413,9 @@ struct Values { | |||
| 411 | std::string web_api_url; | 413 | std::string web_api_url; |
| 412 | std::string yuzu_username; | 414 | std::string yuzu_username; |
| 413 | std::string yuzu_token; | 415 | std::string yuzu_token; |
| 416 | |||
| 417 | // Add-Ons | ||
| 418 | std::map<u64, std::vector<std::string>> disabled_addons; | ||
| 414 | } extern values; | 419 | } extern values; |
| 415 | 420 | ||
| 416 | void Apply(); | 421 | void Apply(); |
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 | |||
| 35 | configuration/configure_mouse_advanced.h | 35 | configuration/configure_mouse_advanced.h |
| 36 | configuration/configure_system.cpp | 36 | configuration/configure_system.cpp |
| 37 | configuration/configure_system.h | 37 | configuration/configure_system.h |
| 38 | configuration/configure_per_general.cpp | ||
| 39 | configuration/configure_per_general.h | ||
| 38 | configuration/configure_touchscreen_advanced.cpp | 40 | configuration/configure_touchscreen_advanced.cpp |
| 39 | configuration/configure_touchscreen_advanced.h | 41 | configuration/configure_touchscreen_advanced.h |
| 40 | configuration/configure_web.cpp | 42 | configuration/configure_web.cpp |
| @@ -86,6 +88,7 @@ set(UIS | |||
| 86 | configuration/configure_input.ui | 88 | configuration/configure_input.ui |
| 87 | configuration/configure_input_player.ui | 89 | configuration/configure_input_player.ui |
| 88 | configuration/configure_mouse_advanced.ui | 90 | configuration/configure_mouse_advanced.ui |
| 91 | configuration/configure_per_general.ui | ||
| 89 | configuration/configure_system.ui | 92 | configuration/configure_system.ui |
| 90 | configuration/configure_touchscreen_advanced.ui | 93 | configuration/configure_touchscreen_advanced.ui |
| 91 | configuration/configure_web.ui | 94 | configuration/configure_web.ui |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index c26161169..eb2077b0f 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -441,6 +441,21 @@ void Config::ReadValues() { | |||
| 441 | Settings::values.yuzu_token = qt_config->value("yuzu_token").toString().toStdString(); | 441 | Settings::values.yuzu_token = qt_config->value("yuzu_token").toString().toStdString(); |
| 442 | qt_config->endGroup(); | 442 | qt_config->endGroup(); |
| 443 | 443 | ||
| 444 | const auto size = qt_config->beginReadArray("DisabledAddOns"); | ||
| 445 | for (int i = 0; i < size; ++i) { | ||
| 446 | qt_config->setArrayIndex(i); | ||
| 447 | const auto title_id = qt_config->value("title_id", 0).toULongLong(); | ||
| 448 | std::vector<std::string> out; | ||
| 449 | const auto d_size = qt_config->beginReadArray("disabled"); | ||
| 450 | for (int j = 0; j < d_size; ++j) { | ||
| 451 | qt_config->setArrayIndex(j); | ||
| 452 | out.push_back(qt_config->value("d", "").toString().toStdString()); | ||
| 453 | } | ||
| 454 | qt_config->endArray(); | ||
| 455 | Settings::values.disabled_addons.insert_or_assign(title_id, out); | ||
| 456 | } | ||
| 457 | qt_config->endArray(); | ||
| 458 | |||
| 444 | qt_config->beginGroup("UI"); | 459 | qt_config->beginGroup("UI"); |
| 445 | UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString(); | 460 | UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString(); |
| 446 | UISettings::values.enable_discord_presence = | 461 | UISettings::values.enable_discord_presence = |
| @@ -645,6 +660,21 @@ void Config::SaveValues() { | |||
| 645 | qt_config->setValue("yuzu_token", QString::fromStdString(Settings::values.yuzu_token)); | 660 | qt_config->setValue("yuzu_token", QString::fromStdString(Settings::values.yuzu_token)); |
| 646 | qt_config->endGroup(); | 661 | qt_config->endGroup(); |
| 647 | 662 | ||
| 663 | qt_config->beginWriteArray("DisabledAddOns"); | ||
| 664 | int i = 0; | ||
| 665 | for (const auto& elem : Settings::values.disabled_addons) { | ||
| 666 | qt_config->setArrayIndex(i); | ||
| 667 | qt_config->setValue("title_id", QVariant::fromValue<u64>(elem.first)); | ||
| 668 | qt_config->beginWriteArray("disabled"); | ||
| 669 | for (std::size_t j = 0; j < elem.second.size(); ++j) { | ||
| 670 | qt_config->setArrayIndex(j); | ||
| 671 | qt_config->setValue("d", QString::fromStdString(elem.second[j])); | ||
| 672 | } | ||
| 673 | qt_config->endArray(); | ||
| 674 | ++i; | ||
| 675 | } | ||
| 676 | qt_config->endArray(); | ||
| 677 | |||
| 648 | qt_config->beginGroup("UI"); | 678 | qt_config->beginGroup("UI"); |
| 649 | qt_config->setValue("theme", UISettings::values.theme); | 679 | qt_config->setValue("theme", UISettings::values.theme); |
| 650 | qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence); | 680 | qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence); |
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 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <memory> | ||
| 7 | #include <utility> | ||
| 8 | |||
| 9 | #include <QHeaderView> | ||
| 10 | #include <QMenu> | ||
| 11 | #include <QMessageBox> | ||
| 12 | #include <QStandardItemModel> | ||
| 13 | #include <QString> | ||
| 14 | #include <QTimer> | ||
| 15 | #include <QTreeView> | ||
| 16 | |||
| 17 | #include "core/file_sys/control_metadata.h" | ||
| 18 | #include "core/file_sys/patch_manager.h" | ||
| 19 | #include "core/file_sys/xts_archive.h" | ||
| 20 | #include "core/loader/loader.h" | ||
| 21 | #include "ui_configure_per_general.h" | ||
| 22 | #include "yuzu/configuration/config.h" | ||
| 23 | #include "yuzu/configuration/configure_input.h" | ||
| 24 | #include "yuzu/configuration/configure_per_general.h" | ||
| 25 | #include "yuzu/ui_settings.h" | ||
| 26 | #include "yuzu/util/util.h" | ||
| 27 | |||
| 28 | ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id) | ||
| 29 | : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGameGeneral>()), title_id(title_id) { | ||
| 30 | |||
| 31 | ui->setupUi(this); | ||
| 32 | setFocusPolicy(Qt::ClickFocus); | ||
| 33 | setWindowTitle(tr("Properties")); | ||
| 34 | |||
| 35 | layout = new QVBoxLayout; | ||
| 36 | tree_view = new QTreeView; | ||
| 37 | item_model = new QStandardItemModel(tree_view); | ||
| 38 | tree_view->setModel(item_model); | ||
| 39 | tree_view->setAlternatingRowColors(true); | ||
| 40 | tree_view->setSelectionMode(QHeaderView::SingleSelection); | ||
| 41 | tree_view->setSelectionBehavior(QHeaderView::SelectRows); | ||
| 42 | tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel); | ||
| 43 | tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel); | ||
| 44 | tree_view->setSortingEnabled(true); | ||
| 45 | tree_view->setEditTriggers(QHeaderView::NoEditTriggers); | ||
| 46 | tree_view->setUniformRowHeights(true); | ||
| 47 | tree_view->setContextMenuPolicy(Qt::NoContextMenu); | ||
| 48 | |||
| 49 | item_model->insertColumns(0, 2); | ||
| 50 | item_model->setHeaderData(0, Qt::Horizontal, "Patch Name"); | ||
| 51 | item_model->setHeaderData(1, Qt::Horizontal, "Version"); | ||
| 52 | |||
| 53 | // We must register all custom types with the Qt Automoc system so that we are able to use it | ||
| 54 | // with signals/slots. In this case, QList falls under the umbrells of custom types. | ||
| 55 | qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); | ||
| 56 | |||
| 57 | layout->setContentsMargins(0, 0, 0, 0); | ||
| 58 | layout->setSpacing(0); | ||
| 59 | layout->addWidget(tree_view); | ||
| 60 | |||
| 61 | ui->scrollArea->setLayout(layout); | ||
| 62 | |||
| 63 | scene = new QGraphicsScene; | ||
| 64 | ui->icon_view->setScene(scene); | ||
| 65 | |||
| 66 | connect(item_model, &QStandardItemModel::itemChanged, | ||
| 67 | [] { UISettings::values.is_game_list_reload_pending.exchange(true); }); | ||
| 68 | |||
| 69 | this->loadConfiguration(); | ||
| 70 | } | ||
| 71 | |||
| 72 | ConfigurePerGameGeneral::~ConfigurePerGameGeneral() = default; | ||
| 73 | |||
| 74 | void ConfigurePerGameGeneral::applyConfiguration() { | ||
| 75 | std::vector<std::string> disabled_addons; | ||
| 76 | |||
| 77 | for (const auto& item : list_items) { | ||
| 78 | const auto disabled = item.front()->checkState() == Qt::Unchecked; | ||
| 79 | if (disabled) | ||
| 80 | disabled_addons.push_back(item.front()->text().toStdString()); | ||
| 81 | } | ||
| 82 | |||
| 83 | Settings::values.disabled_addons[title_id] = disabled_addons; | ||
| 84 | } | ||
| 85 | |||
| 86 | void ConfigurePerGameGeneral::loadFromFile(FileSys::VirtualFile file) { | ||
| 87 | this->file = std::move(file); | ||
| 88 | this->loadConfiguration(); | ||
| 89 | } | ||
| 90 | |||
| 91 | void ConfigurePerGameGeneral::loadConfiguration() { | ||
| 92 | if (file == nullptr) | ||
| 93 | return; | ||
| 94 | |||
| 95 | const auto loader = Loader::GetLoader(file); | ||
| 96 | |||
| 97 | ui->display_title_id->setText(fmt::format("{:016X}", title_id).c_str()); | ||
| 98 | |||
| 99 | FileSys::PatchManager pm{title_id}; | ||
| 100 | const auto control = pm.GetControlMetadata(); | ||
| 101 | |||
| 102 | if (control.first != nullptr) { | ||
| 103 | ui->display_version->setText(QString::fromStdString(control.first->GetVersionString())); | ||
| 104 | ui->display_name->setText(QString::fromStdString(control.first->GetApplicationName())); | ||
| 105 | ui->display_developer->setText(QString::fromStdString(control.first->GetDeveloperName())); | ||
| 106 | } else { | ||
| 107 | std::string title; | ||
| 108 | if (loader->ReadTitle(title) == Loader::ResultStatus::Success) | ||
| 109 | ui->display_name->setText(QString::fromStdString(title)); | ||
| 110 | |||
| 111 | std::string developer; | ||
| 112 | if (loader->ReadDeveloper(developer) == Loader::ResultStatus::Success) | ||
| 113 | ui->display_developer->setText(QString::fromStdString(developer)); | ||
| 114 | |||
| 115 | ui->display_version->setText(QStringLiteral("1.0.0")); | ||
| 116 | } | ||
| 117 | |||
| 118 | if (control.second != nullptr) { | ||
| 119 | scene->clear(); | ||
| 120 | |||
| 121 | QPixmap map; | ||
| 122 | const auto bytes = control.second->ReadAllBytes(); | ||
| 123 | map.loadFromData(bytes.data(), bytes.size()); | ||
| 124 | |||
| 125 | scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(), | ||
| 126 | Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); | ||
| 127 | } else { | ||
| 128 | std::vector<u8> bytes; | ||
| 129 | if (loader->ReadIcon(bytes) == Loader::ResultStatus::Success) { | ||
| 130 | scene->clear(); | ||
| 131 | |||
| 132 | QPixmap map; | ||
| 133 | map.loadFromData(bytes.data(), bytes.size()); | ||
| 134 | |||
| 135 | scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(), | ||
| 136 | Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | FileSys::VirtualFile update_raw; | ||
| 141 | loader->ReadUpdateRaw(update_raw); | ||
| 142 | |||
| 143 | const auto& disabled = Settings::values.disabled_addons[title_id]; | ||
| 144 | |||
| 145 | for (const auto& patch : pm.GetPatchVersionNames(update_raw)) { | ||
| 146 | QStandardItem* first_item = new QStandardItem; | ||
| 147 | const auto name = QString::fromStdString(patch.first).replace("[D] ", ""); | ||
| 148 | first_item->setText(name); | ||
| 149 | first_item->setCheckable(true); | ||
| 150 | |||
| 151 | const auto patch_disabled = | ||
| 152 | std::find(disabled.begin(), disabled.end(), name.toStdString()) != disabled.end(); | ||
| 153 | |||
| 154 | first_item->setCheckState(patch_disabled ? Qt::Unchecked : Qt::Checked); | ||
| 155 | |||
| 156 | list_items.push_back(QList<QStandardItem*>{ | ||
| 157 | first_item, new QStandardItem{QString::fromStdString(patch.second)}}); | ||
| 158 | item_model->appendRow(list_items.back()); | ||
| 159 | } | ||
| 160 | |||
| 161 | tree_view->setColumnWidth(0, 5 * tree_view->width() / 16); | ||
| 162 | |||
| 163 | ui->display_filename->setText(QString::fromStdString(file->GetName())); | ||
| 164 | |||
| 165 | ui->display_format->setText( | ||
| 166 | QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))); | ||
| 167 | |||
| 168 | const auto valueText = ReadableByteSize(file->GetSize()); | ||
| 169 | ui->display_size->setText(valueText); | ||
| 170 | } | ||
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 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 10 | #include <QKeyEvent> | ||
| 11 | #include <QList> | ||
| 12 | #include <QWidget> | ||
| 13 | |||
| 14 | #include "core/file_sys/vfs_types.h" | ||
| 15 | |||
| 16 | class QTreeView; | ||
| 17 | class QGraphicsScene; | ||
| 18 | class QStandardItem; | ||
| 19 | class QStandardItemModel; | ||
| 20 | |||
| 21 | namespace Ui { | ||
| 22 | class ConfigurePerGameGeneral; | ||
| 23 | } | ||
| 24 | |||
| 25 | class ConfigurePerGameGeneral : public QDialog { | ||
| 26 | Q_OBJECT | ||
| 27 | |||
| 28 | public: | ||
| 29 | explicit ConfigurePerGameGeneral(QWidget* parent, u64 title_id); | ||
| 30 | ~ConfigurePerGameGeneral() override; | ||
| 31 | |||
| 32 | /// Save all button configurations to settings file | ||
| 33 | void applyConfiguration(); | ||
| 34 | |||
| 35 | void loadFromFile(FileSys::VirtualFile file); | ||
| 36 | |||
| 37 | private: | ||
| 38 | std::unique_ptr<Ui::ConfigurePerGameGeneral> ui; | ||
| 39 | FileSys::VirtualFile file; | ||
| 40 | u64 title_id; | ||
| 41 | |||
| 42 | QVBoxLayout* layout; | ||
| 43 | QTreeView* tree_view; | ||
| 44 | QStandardItemModel* item_model; | ||
| 45 | QGraphicsScene* scene; | ||
| 46 | |||
| 47 | std::vector<QList<QStandardItem*>> list_items; | ||
| 48 | |||
| 49 | void loadConfiguration(); | ||
| 50 | }; | ||
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 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <ui version="4.0"> | ||
| 3 | <class>ConfigurePerGameGeneral</class> | ||
| 4 | <widget class="QDialog" name="ConfigurePerGameGeneral"> | ||
| 5 | <property name="geometry"> | ||
| 6 | <rect> | ||
| 7 | <x>0</x> | ||
| 8 | <y>0</y> | ||
| 9 | <width>400</width> | ||
| 10 | <height>520</height> | ||
| 11 | </rect> | ||
| 12 | </property> | ||
| 13 | <property name="windowTitle"> | ||
| 14 | <string>ConfigurePerGameGeneral</string> | ||
| 15 | </property> | ||
| 16 | <layout class="QHBoxLayout" name="HorizontalLayout"> | ||
| 17 | <item> | ||
| 18 | <layout class="QVBoxLayout" name="VerticalLayout"> | ||
| 19 | <item> | ||
| 20 | <widget class="QGroupBox" name="GeneralGroupBox"> | ||
| 21 | <property name="title"> | ||
| 22 | <string>Info</string> | ||
| 23 | </property> | ||
| 24 | <layout class="QHBoxLayout" name="GeneralHorizontalLayout"> | ||
| 25 | <item> | ||
| 26 | <layout class="QGridLayout" name="gridLayout_2"> | ||
| 27 | <item row="6" column="1" colspan="2"> | ||
| 28 | <widget class="QLineEdit" name="display_filename"> | ||
| 29 | <property name="enabled"> | ||
| 30 | <bool>true</bool> | ||
| 31 | </property> | ||
| 32 | <property name="readOnly"> | ||
| 33 | <bool>true</bool> | ||
| 34 | </property> | ||
| 35 | </widget> | ||
| 36 | </item> | ||
| 37 | <item row="0" column="1"> | ||
| 38 | <widget class="QLineEdit" name="display_name"> | ||
| 39 | <property name="enabled"> | ||
| 40 | <bool>true</bool> | ||
| 41 | </property> | ||
| 42 | <property name="readOnly"> | ||
| 43 | <bool>true</bool> | ||
| 44 | </property> | ||
| 45 | </widget> | ||
| 46 | </item> | ||
| 47 | <item row="1" column="0"> | ||
| 48 | <widget class="QLabel" name="label_2"> | ||
| 49 | <property name="text"> | ||
| 50 | <string>Developer</string> | ||
| 51 | </property> | ||
| 52 | </widget> | ||
| 53 | </item> | ||
| 54 | <item row="5" column="1" colspan="2"> | ||
| 55 | <widget class="QLineEdit" name="display_size"> | ||
| 56 | <property name="enabled"> | ||
| 57 | <bool>true</bool> | ||
| 58 | </property> | ||
| 59 | <property name="readOnly"> | ||
| 60 | <bool>true</bool> | ||
| 61 | </property> | ||
| 62 | </widget> | ||
| 63 | </item> | ||
| 64 | <item row="0" column="0"> | ||
| 65 | <widget class="QLabel" name="label"> | ||
| 66 | <property name="text"> | ||
| 67 | <string>Name</string> | ||
| 68 | </property> | ||
| 69 | </widget> | ||
| 70 | </item> | ||
| 71 | <item row="6" column="0"> | ||
| 72 | <widget class="QLabel" name="label_7"> | ||
| 73 | <property name="text"> | ||
| 74 | <string>Filename</string> | ||
| 75 | </property> | ||
| 76 | </widget> | ||
| 77 | </item> | ||
| 78 | <item row="2" column="0"> | ||
| 79 | <widget class="QLabel" name="label_3"> | ||
| 80 | <property name="text"> | ||
| 81 | <string>Version</string> | ||
| 82 | </property> | ||
| 83 | </widget> | ||
| 84 | </item> | ||
| 85 | <item row="4" column="0"> | ||
| 86 | <widget class="QLabel" name="label_5"> | ||
| 87 | <property name="text"> | ||
| 88 | <string>Format</string> | ||
| 89 | </property> | ||
| 90 | </widget> | ||
| 91 | </item> | ||
| 92 | <item row="2" column="1"> | ||
| 93 | <widget class="QLineEdit" name="display_version"> | ||
| 94 | <property name="enabled"> | ||
| 95 | <bool>true</bool> | ||
| 96 | </property> | ||
| 97 | <property name="readOnly"> | ||
| 98 | <bool>true</bool> | ||
| 99 | </property> | ||
| 100 | </widget> | ||
| 101 | </item> | ||
| 102 | <item row="4" column="1"> | ||
| 103 | <widget class="QLineEdit" name="display_format"> | ||
| 104 | <property name="enabled"> | ||
| 105 | <bool>true</bool> | ||
| 106 | </property> | ||
| 107 | <property name="readOnly"> | ||
| 108 | <bool>true</bool> | ||
| 109 | </property> | ||
| 110 | </widget> | ||
| 111 | </item> | ||
| 112 | <item row="5" column="0"> | ||
| 113 | <widget class="QLabel" name="label_6"> | ||
| 114 | <property name="text"> | ||
| 115 | <string>Size</string> | ||
| 116 | </property> | ||
| 117 | </widget> | ||
| 118 | </item> | ||
| 119 | <item row="1" column="1"> | ||
| 120 | <widget class="QLineEdit" name="display_developer"> | ||
| 121 | <property name="enabled"> | ||
| 122 | <bool>true</bool> | ||
| 123 | </property> | ||
| 124 | <property name="readOnly"> | ||
| 125 | <bool>true</bool> | ||
| 126 | </property> | ||
| 127 | </widget> | ||
| 128 | </item> | ||
| 129 | <item row="3" column="0"> | ||
| 130 | <widget class="QLabel" name="label_4"> | ||
| 131 | <property name="text"> | ||
| 132 | <string>Title ID</string> | ||
| 133 | </property> | ||
| 134 | </widget> | ||
| 135 | </item> | ||
| 136 | <item row="3" column="1"> | ||
| 137 | <widget class="QLineEdit" name="display_title_id"> | ||
| 138 | <property name="enabled"> | ||
| 139 | <bool>true</bool> | ||
| 140 | </property> | ||
| 141 | <property name="readOnly"> | ||
| 142 | <bool>true</bool> | ||
| 143 | </property> | ||
| 144 | </widget> | ||
| 145 | </item> | ||
| 146 | <item row="0" column="2" rowspan="5"> | ||
| 147 | <widget class="QGraphicsView" name="icon_view"> | ||
| 148 | <property name="sizePolicy"> | ||
| 149 | <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> | ||
| 150 | <horstretch>0</horstretch> | ||
| 151 | <verstretch>0</verstretch> | ||
| 152 | </sizepolicy> | ||
| 153 | </property> | ||
| 154 | <property name="minimumSize"> | ||
| 155 | <size> | ||
| 156 | <width>128</width> | ||
| 157 | <height>128</height> | ||
| 158 | </size> | ||
| 159 | </property> | ||
| 160 | <property name="maximumSize"> | ||
| 161 | <size> | ||
| 162 | <width>128</width> | ||
| 163 | <height>128</height> | ||
| 164 | </size> | ||
| 165 | </property> | ||
| 166 | <property name="verticalScrollBarPolicy"> | ||
| 167 | <enum>Qt::ScrollBarAlwaysOff</enum> | ||
| 168 | </property> | ||
| 169 | <property name="horizontalScrollBarPolicy"> | ||
| 170 | <enum>Qt::ScrollBarAlwaysOff</enum> | ||
| 171 | </property> | ||
| 172 | <property name="sizeAdjustPolicy"> | ||
| 173 | <enum>QAbstractScrollArea::AdjustToContents</enum> | ||
| 174 | </property> | ||
| 175 | <property name="interactive"> | ||
| 176 | <bool>false</bool> | ||
| 177 | </property> | ||
| 178 | </widget> | ||
| 179 | </item> | ||
| 180 | </layout> | ||
| 181 | </item> | ||
| 182 | </layout> | ||
| 183 | </widget> | ||
| 184 | </item> | ||
| 185 | <item> | ||
| 186 | <widget class="QGroupBox" name="PerformanceGroupBox"> | ||
| 187 | <property name="title"> | ||
| 188 | <string>Add-Ons</string> | ||
| 189 | </property> | ||
| 190 | <layout class="QHBoxLayout" name="PerformanceHorizontalLayout"> | ||
| 191 | <item> | ||
| 192 | <widget class="QScrollArea" name="scrollArea"> | ||
| 193 | <property name="widgetResizable"> | ||
| 194 | <bool>true</bool> | ||
| 195 | </property> | ||
| 196 | <widget class="QWidget" name="scrollAreaWidgetContents"> | ||
| 197 | <property name="geometry"> | ||
| 198 | <rect> | ||
| 199 | <x>0</x> | ||
| 200 | <y>0</y> | ||
| 201 | <width>350</width> | ||
| 202 | <height>169</height> | ||
| 203 | </rect> | ||
| 204 | </property> | ||
| 205 | </widget> | ||
| 206 | </widget> | ||
| 207 | </item> | ||
| 208 | <item> | ||
| 209 | <layout class="QVBoxLayout" name="PerformanceVerticalLayout"/> | ||
| 210 | </item> | ||
| 211 | </layout> | ||
| 212 | </widget> | ||
| 213 | </item> | ||
| 214 | <item> | ||
| 215 | <spacer name="verticalSpacer"> | ||
| 216 | <property name="orientation"> | ||
| 217 | <enum>Qt::Vertical</enum> | ||
| 218 | </property> | ||
| 219 | <property name="sizeType"> | ||
| 220 | <enum>QSizePolicy::Fixed</enum> | ||
| 221 | </property> | ||
| 222 | <property name="sizeHint" stdset="0"> | ||
| 223 | <size> | ||
| 224 | <width>20</width> | ||
| 225 | <height>40</height> | ||
| 226 | </size> | ||
| 227 | </property> | ||
| 228 | </spacer> | ||
| 229 | </item> | ||
| 230 | <item> | ||
| 231 | <widget class="QDialogButtonBox" name="buttonBox"> | ||
| 232 | <property name="standardButtons"> | ||
| 233 | <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||
| 234 | </property> | ||
| 235 | </widget> | ||
| 236 | </item> | ||
| 237 | </layout> | ||
| 238 | </item> | ||
| 239 | </layout> | ||
| 240 | </widget> | ||
| 241 | <resources/> | ||
| 242 | <connections> | ||
| 243 | <connection> | ||
| 244 | <sender>buttonBox</sender> | ||
| 245 | <signal>accepted()</signal> | ||
| 246 | <receiver>ConfigurePerGameGeneral</receiver> | ||
| 247 | <slot>accept()</slot> | ||
| 248 | <hints> | ||
| 249 | <hint type="sourcelabel"> | ||
| 250 | <x>269</x> | ||
| 251 | <y>567</y> | ||
| 252 | </hint> | ||
| 253 | <hint type="destinationlabel"> | ||
| 254 | <x>269</x> | ||
| 255 | <y>294</y> | ||
| 256 | </hint> | ||
| 257 | </hints> | ||
| 258 | </connection> | ||
| 259 | <connection> | ||
| 260 | <sender>buttonBox</sender> | ||
| 261 | <signal>rejected()</signal> | ||
| 262 | <receiver>ConfigurePerGameGeneral</receiver> | ||
| 263 | <slot>reject()</slot> | ||
| 264 | <hints> | ||
| 265 | <hint type="sourcelabel"> | ||
| 266 | <x>269</x> | ||
| 267 | <y>567</y> | ||
| 268 | </hint> | ||
| 269 | <hint type="destinationlabel"> | ||
| 270 | <x>269</x> | ||
| 271 | <y>294</y> | ||
| 272 | </hint> | ||
| 273 | </hints> | ||
| 274 | </connection> | ||
| 275 | </connections> | ||
| 276 | </ui> | ||
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) { | |||
| 333 | QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); | 333 | QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); |
| 334 | QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); | 334 | QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); |
| 335 | QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); | 335 | QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); |
| 336 | context_menu.addSeparator(); | ||
| 337 | QAction* properties = context_menu.addAction(tr("Properties")); | ||
| 336 | 338 | ||
| 337 | open_save_location->setEnabled(program_id != 0); | 339 | open_save_location->setEnabled(program_id != 0); |
| 338 | auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); | 340 | auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); |
| @@ -346,6 +348,7 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { | |||
| 346 | connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); }); | 348 | connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); }); |
| 347 | connect(navigate_to_gamedb_entry, &QAction::triggered, | 349 | connect(navigate_to_gamedb_entry, &QAction::triggered, |
| 348 | [&]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); }); | 350 | [&]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); }); |
| 351 | connect(properties, &QAction::triggered, [&]() { emit OpenPerGameGeneralRequested(path); }); | ||
| 349 | 352 | ||
| 350 | context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location)); | 353 | context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location)); |
| 351 | } | 354 | } |
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: | |||
| 70 | void CopyTIDRequested(u64 program_id); | 70 | void CopyTIDRequested(u64 program_id); |
| 71 | void NavigateToGamedbEntryRequested(u64 program_id, | 71 | void NavigateToGamedbEntryRequested(u64 program_id, |
| 72 | const CompatibilityList& compatibility_list); | 72 | const CompatibilityList& compatibility_list); |
| 73 | void OpenPerGameGeneralRequested(const std::string& file); | ||
| 73 | 74 | ||
| 74 | private slots: | 75 | private slots: |
| 75 | void onTextChanged(const QString& newText); | 76 | void onTextChanged(const QString& newText); |
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 20f5e8798..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, | |||
| 62 | FileSys::VirtualFile update_raw; | 62 | FileSys::VirtualFile update_raw; |
| 63 | loader.ReadUpdateRaw(update_raw); | 63 | loader.ReadUpdateRaw(update_raw); |
| 64 | for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) { | 64 | for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) { |
| 65 | const bool is_update = kv.first == "Update"; | 65 | const bool is_update = kv.first == "Update" || kv.first == "[D] Update"; |
| 66 | if (!updatable && is_update) { | 66 | if (!updatable && is_update) { |
| 67 | continue; | 67 | continue; |
| 68 | } | 68 | } |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 22c207a3a..90b212ba5 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | 9 | ||
| 10 | // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. | 10 | // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. |
| 11 | #include "applets/software_keyboard.h" | 11 | #include "applets/software_keyboard.h" |
| 12 | #include "configuration/configure_per_general.h" | ||
| 12 | #include "core/file_sys/vfs.h" | 13 | #include "core/file_sys/vfs.h" |
| 13 | #include "core/file_sys/vfs_real.h" | 14 | #include "core/file_sys/vfs_real.h" |
| 14 | #include "core/hle/service/acc/profile_manager.h" | 15 | #include "core/hle/service/acc/profile_manager.h" |
| @@ -441,6 +442,8 @@ void GMainWindow::ConnectWidgetEvents() { | |||
| 441 | connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); | 442 | connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); |
| 442 | connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, | 443 | connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, |
| 443 | &GMainWindow::OnGameListNavigateToGamedbEntry); | 444 | &GMainWindow::OnGameListNavigateToGamedbEntry); |
| 445 | connect(game_list, &GameList::OpenPerGameGeneralRequested, this, | ||
| 446 | &GMainWindow::OnGameListOpenPerGameProperties); | ||
| 444 | 447 | ||
| 445 | connect(this, &GMainWindow::EmulationStarting, render_window, | 448 | connect(this, &GMainWindow::EmulationStarting, render_window, |
| 446 | &GRenderWindow::OnEmulationStarting); | 449 | &GRenderWindow::OnEmulationStarting); |
| @@ -988,6 +991,32 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, | |||
| 988 | QDesktopServices::openUrl(QUrl("https://yuzu-emu.org/game/" + directory)); | 991 | QDesktopServices::openUrl(QUrl("https://yuzu-emu.org/game/" + directory)); |
| 989 | } | 992 | } |
| 990 | 993 | ||
| 994 | void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { | ||
| 995 | u64 title_id{}; | ||
| 996 | const auto v_file = Core::GetGameFileFromPath(vfs, file); | ||
| 997 | const auto loader = Loader::GetLoader(v_file); | ||
| 998 | if (loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) { | ||
| 999 | QMessageBox::information(this, tr("Properties"), | ||
| 1000 | tr("The game properties could not be loaded.")); | ||
| 1001 | return; | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | ConfigurePerGameGeneral dialog(this, title_id); | ||
| 1005 | dialog.loadFromFile(v_file); | ||
| 1006 | auto result = dialog.exec(); | ||
| 1007 | if (result == QDialog::Accepted) { | ||
| 1008 | dialog.applyConfiguration(); | ||
| 1009 | |||
| 1010 | const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); | ||
| 1011 | if (reload) { | ||
| 1012 | game_list->PopulateAsync(UISettings::values.gamedir, | ||
| 1013 | UISettings::values.gamedir_deepscan); | ||
| 1014 | } | ||
| 1015 | |||
| 1016 | config->Save(); | ||
| 1017 | } | ||
| 1018 | } | ||
| 1019 | |||
| 991 | void GMainWindow::OnMenuLoadFile() { | 1020 | void GMainWindow::OnMenuLoadFile() { |
| 992 | const QString extensions = | 1021 | const QString extensions = |
| 993 | QString("*.").append(GameList::supported_file_extensions.join(" *.")).append(" main"); | 1022 | 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: | |||
| 168 | void OnGameListCopyTID(u64 program_id); | 168 | void OnGameListCopyTID(u64 program_id); |
| 169 | void OnGameListNavigateToGamedbEntry(u64 program_id, | 169 | void OnGameListNavigateToGamedbEntry(u64 program_id, |
| 170 | const CompatibilityList& compatibility_list); | 170 | const CompatibilityList& compatibility_list); |
| 171 | void OnGameListOpenPerGameProperties(const std::string& file); | ||
| 171 | void OnMenuLoadFile(); | 172 | void OnMenuLoadFile(); |
| 172 | void OnMenuLoadFolder(); | 173 | void OnMenuLoadFolder(); |
| 173 | void OnMenuInstallToNAND(); | 174 | 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 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include <sstream> | ||
| 6 | #include <SDL.h> | 7 | #include <SDL.h> |
| 7 | #include <inih/cpp/INIReader.h> | 8 | #include <inih/cpp/INIReader.h> |
| 8 | #include "common/file_util.h" | 9 | #include "common/file_util.h" |
| @@ -369,6 +370,23 @@ void Config::ReadValues() { | |||
| 369 | Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false); | 370 | Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false); |
| 370 | Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); | 371 | Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); |
| 371 | 372 | ||
| 373 | const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); | ||
| 374 | std::stringstream ss(title_list); | ||
| 375 | std::string line; | ||
| 376 | while (std::getline(ss, line, '|')) { | ||
| 377 | const auto title_id = std::stoul(line, nullptr, 16); | ||
| 378 | const auto disabled_list = sdl2_config->Get("AddOns", "disabled_" + line, ""); | ||
| 379 | |||
| 380 | std::stringstream inner_ss(disabled_list); | ||
| 381 | std::string inner_line; | ||
| 382 | std::vector<std::string> out; | ||
| 383 | while (std::getline(inner_ss, inner_line, '|')) { | ||
| 384 | out.push_back(inner_line); | ||
| 385 | } | ||
| 386 | |||
| 387 | Settings::values.disabled_addons.insert_or_assign(title_id, out); | ||
| 388 | } | ||
| 389 | |||
| 372 | // Web Service | 390 | // Web Service |
| 373 | Settings::values.enable_telemetry = | 391 | Settings::values.enable_telemetry = |
| 374 | sdl2_config->GetBoolean("WebService", "enable_telemetry", true); | 392 | 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 | |||
| 221 | # See https://profile.yuzu-emu.org/ for more info | 221 | # See https://profile.yuzu-emu.org/ for more info |
| 222 | yuzu_username = | 222 | yuzu_username = |
| 223 | yuzu_token = | 223 | yuzu_token = |
| 224 | |||
| 225 | [AddOns] | ||
| 226 | # Used to disable add-ons | ||
| 227 | # List of title IDs of games that will have add-ons disabled (separated by '|'): | ||
| 228 | title_ids = | ||
| 229 | # 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 '|') | ||
| 230 | # e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey | ||
| 224 | )"; | 231 | )"; |
| 225 | } | 232 | } |