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/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) {}
 
 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,11 +77,15 @@ 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);
 
-    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)));
@@ -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));
@@ -282,7 +302,12 @@ 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& 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) {
@@ -290,7 +315,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
                      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 (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,7 +408,9 @@ 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);
         }
     }
 
@@ -401,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;
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/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 @@
 #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 {
 
@@ -76,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); })));
@@ -96,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.
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 @@
 #include <vector>
 
 #include "common/common_types.h"
+#include "core/file_sys/control_metadata.h"
 #include "core/file_sys/vfs.h"
 
 namespace Kernel {
@@ -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/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 9ed8a59cf..2b1e0719b 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -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 ded5bb88a..15d1b1a23 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -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/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/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 c26161169..eb2077b0f 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -441,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 =
@@ -645,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_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/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 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,
     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;
         }
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 @@
 
 // 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);
@@ -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
 )";
 }