diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index f66467d410..33b4a9faaa 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -1,6 +1,9 @@
 # SPDX-FileCopyrightText: 2018 yuzu Emulator Project
 # SPDX-License-Identifier: GPL-2.0-or-later
 
+# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+# SPDX-License-Identifier: GPL-3.0-or-later
+
 add_library(core STATIC
     arm/arm_interface.cpp
     arm/arm_interface.h
@@ -175,6 +178,8 @@ add_library(core STATIC
     file_sys/vfs/vfs_vector.h
     file_sys/xts_archive.cpp
     file_sys/xts_archive.h
+    file_sys/external_content_manager.cpp
+    file_sys/external_content_manager.h
     frontend/applets/cabinet.cpp
     frontend/applets/cabinet.h
     frontend/applets/controller.cpp
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 6652523589..7202a6d6c1 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -1,6 +1,9 @@
 // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
 #include <algorithm>
 #include <cstring>
 #include <optional>
@@ -117,9 +120,7 @@ NCA::NCA(VirtualFile file_, const NCA* base_nca)
     }
 
     if (is_update && base_nca == nullptr) {
-        status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
-    } else {
-        status = Loader::ResultStatus::Success;
+        // Ignore this for now to allow external addOns; it shouldn't affect anything
     }
 }
 
diff --git a/src/core/file_sys/external_content_manager.cpp b/src/core/file_sys/external_content_manager.cpp
new file mode 100644
index 0000000000..114fa4c648
--- /dev/null
+++ b/src/core/file_sys/external_content_manager.cpp
@@ -0,0 +1,387 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <algorithm>
+#include <filesystem>
+#include <fstream>
+#include <memory>
+#include <string>
+#include <set>
+#include <regex>
+#include "common/fs/file.h"
+#include "common/fs/fs.h"
+#include "common/logging/log.h"
+#include "common/string_util.h"
+#include "core/file_sys/external_content_manager.h"
+#include "core/file_sys/content_archive.h"
+#include "core/file_sys/nca_metadata.h"
+#include "core/file_sys/registered_cache.h"
+#include "core/file_sys/romfs.h"
+#include "core/file_sys/submission_package.h"
+#include "core/file_sys/vfs/vfs.h"
+#include "core/file_sys/vfs/vfs_real.h"
+#include "core/loader/loader.h"
+#include "core/file_sys/common_funcs.h"
+
+namespace FileSys {
+
+ExternalContentManager::ExternalContentManager() {
+    vfs = std::make_shared<RealVfsFilesystem>();
+}
+
+ExternalContentManager::~ExternalContentManager() = default;
+
+bool ExternalContentManager::RegisterExternalNSP(const std::string& path, u64 program_id) {
+    if (registered_paths.count(path)) {
+        return true;
+    }
+
+    if (path.empty()) {
+        LOG_ERROR(Loader, "Empty path provided to RegisterExternalNSPInternal");
+        return false;
+    }
+
+    if (!Common::FS::Exists(path)) {
+        LOG_ERROR(Loader, "File does not exist: {}", path);
+        return false;
+    }
+
+    auto file = vfs->OpenFile(path, FileSys::OpenMode::Read);
+    if (file == nullptr) {
+        LOG_ERROR(Loader, "Failed to open NSP file with VFS: {}", path);
+        return false;
+    }
+
+    std::shared_ptr<NSP> nsp;
+    try {
+        nsp = std::make_shared<NSP>(file);
+        if (nsp->GetStatus() != Loader::ResultStatus::Success) {
+            LOG_ERROR(Loader, "Failed to parse NSP file: {} (status: {})",
+                      path, static_cast<int>(nsp->GetStatus()));
+            return false;
+        }
+    } catch (const std::exception& e) {
+        LOG_CRITICAL(Loader, "Exception when parsing NSP file {}: {}", path, e.what());
+        return false;
+    }
+
+    u64 target_program_id = program_id;
+    if (target_program_id == 0) {
+        try {
+            target_program_id = ExtractProgramIDFromFile(nsp);
+        } catch (const std::exception& e) {
+            LOG_ERROR(Loader, "Exception when getting program ID from NSP {}: {}", path, e.what());
+            return false;
+        }
+    }
+
+    external_files[target_program_id].emplace_back(path, file);
+    registered_paths.insert(path);
+
+    ParseExternalFile(path, target_program_id);
+    return true;
+}
+
+bool ExternalContentManager::RequestRegisterExternalNSP(const std::string& path, u64 program_id) {
+    std::lock_guard lock{mutex};
+    return RegisterExternalNSP(path, program_id);
+}
+
+void ExternalContentManager::LoadRegisteredPaths(const std::string& config_path) {
+    std::lock_guard lock{mutex};
+    registered_paths.clear();
+    external_files.clear();
+    update_to_base_map.clear();
+    dlc_to_base_map.clear();
+    file_metadata.clear();
+
+    std::ifstream file(config_path);
+    if (!file.is_open()) {
+        LOG_WARNING(Service_FS, "Could not open external files config for loading: {}", config_path);
+        return;
+    }
+
+    std::string path;
+    std::set<std::string> valid_paths_from_config;
+
+    while (std::getline(file, path)) {
+        if (path.empty()) continue;
+        if (!Common::FS::Exists(path)) {
+            LOG_WARNING(Loader, "External file path from config not found, skipping: {}", path);
+            continue;
+        }
+
+        valid_paths_from_config.insert(path);
+
+        const auto extension = Common::ToLower(std::filesystem::path(path).extension().string());
+        bool success = false;
+        if (extension == ".nsp") {
+            success = RegisterExternalNSP(path, 0);
+        }
+
+        if (!success) {
+            LOG_ERROR(Loader, "Failed to re-register external NSP from config: {}", path);
+            valid_paths_from_config.erase(path);
+        }
+    }
+
+    file.close();
+    registered_paths = std::move(valid_paths_from_config);
+}
+
+void ExternalContentManager::SaveRegisteredPaths(const std::string& config_path) const {
+    std::lock_guard lock{mutex};
+
+    std::ofstream file(config_path);
+    if (!file.is_open()) {
+        LOG_ERROR(Frontend, "Could not open external files config for saving: {}", config_path);
+        return;
+    }
+
+    for (const auto& path : registered_paths) {
+        if (Common::FS::Exists(path)) {
+             const auto extension = Common::ToLower(std::filesystem::path(path).extension().string());
+             if (extension == ".nsp") { // Only save NSP paths
+                file << path << std::endl;
+            }
+        }
+    }
+
+    file.close();
+}
+
+std::set<std::string> ExternalContentManager::GetAllRegisteredPaths() const {
+     std::lock_guard lock{mutex};
+     return registered_paths;
+}
+
+/**
+ * @brief Checks if a given base program ID exists as a value in the map.
+ * @tparam MapType The type of the map (e.g., std::unordered_map<u64, u64>)
+ * @param map The map to search (e.g., update_to_base_map or dlc_to_base_map)
+ * @param program_id The base program ID to look for as a value in the map
+ * @return true if the program_id is found as a base ID in the map, false otherwise.
+ */
+template <typename MapType>
+bool ExternalContentManager::CheckMapForBaseID(const MapType& map, u64 program_id) const {
+    for (const auto& [content_id, base_id] : map) {
+        if (base_id == program_id) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool ExternalContentManager::HasExternalUpdate(u64 program_id) const {
+    std::lock_guard lock{mutex};
+    return CheckMapForBaseID(update_to_base_map, program_id);
+}
+
+bool ExternalContentManager::HasExternalDLC(u64 program_id) const {
+    std::lock_guard lock{mutex};
+    const bool has_dlc = CheckMapForBaseID(dlc_to_base_map, program_id);
+    return has_dlc;
+}
+
+std::vector<std::shared_ptr<VfsFile>> ExternalContentManager::GetExternalFiles(
+    u64 program_id, ExternalContentType content_type, bool first_only = false) const {
+    std::lock_guard lock{mutex};
+
+    std::vector<std::shared_ptr<VfsFile>> result;
+
+    const auto& map = (content_type == ExternalContentType::Update) ?
+                       update_to_base_map : dlc_to_base_map;
+
+    for (const auto& [content_id, base_id] : map) {
+        if (base_id == program_id) {
+            auto it = external_files.find(content_id);
+            if (it != external_files.end() && !it->second.empty()) {
+                if (first_only) {
+                    // Update
+                    result.push_back(it->second.front().second);
+                    break;
+                }
+                // DLC
+                for (const auto& [path, file] : it->second) {
+                    result.push_back(file);
+                }
+            }
+        }
+    }
+
+    return result;
+}
+
+std::shared_ptr<VfsFile> ExternalContentManager::GetExternalUpdateFile(u64 program_id) const {
+    auto files = GetExternalFiles(program_id, ExternalContentType::Update, true);
+    return files.empty() ? nullptr : files.front();
+}
+
+std::vector<std::shared_ptr<VfsFile>> ExternalContentManager::GetExternalDLCFiles(u64 program_id) const {
+    return GetExternalFiles(program_id, ExternalContentType::DLC, false);
+}
+
+void ExternalContentManager::ParseExternalFile(const std::string& path, u64 program_id) {
+    ExternalFileMetadata metadata;
+    std::string filename = std::filesystem::path(path).filename().string();
+
+    const auto extension = std::filesystem::path(path).extension().string();
+
+    // Should never happen because non nsp files are blocked by the frontend but just in case
+    if (Common::ToLower(extension) != ".nsp") {
+        LOG_CRITICAL(Loader, "Only NSP files are supported: {}", path);
+        return;
+    }
+
+    auto file = vfs->OpenFile(path, FileSys::OpenMode::Read);
+    if (file == nullptr) {
+        LOG_ERROR(Loader, "Failed to open file for parsing: {}", path);
+        return;
+    }
+
+    const auto program_id_low = program_id & 0xFFFFFFFF;
+    const bool is_update = (program_id_low & 0x800) != 0 && (program_id_low & 0xFFF) < 0x1000;
+    const bool is_dlc = (program_id_low & 0xF000) != 0 || (GetBaseTitleID(program_id) != program_id);
+
+    metadata.name = is_update ?
+        fmt::format("Update (File): {}", filename) :
+        (is_dlc ? fmt::format("DLC (File): {}", filename) : filename);
+
+    try {
+        auto nsp = std::make_shared<FileSys::NSP>(file);
+        if (nsp->GetStatus() != Loader::ResultStatus::Success) {
+            LOG_WARNING(Loader, "Could not parse NSP for metadata: {}", path);
+            file_metadata[program_id] = metadata;
+            return;
+        }
+
+        const TitleType title_type = is_update ? TitleType::Update :
+                                   (is_dlc ? TitleType::AOC : TitleType::Application);
+
+        // For updates, we need program_id but for DLC we need the actual program_id (not OR'd with 0x800)
+        const u64 control_id = is_update ? program_id :
+                              (is_dlc ? program_id : (program_id | 0x800));
+
+        auto control = nsp->GetNCA(control_id, ContentRecordType::Control, title_type);
+
+        if (control && control->GetStatus() == Loader::ResultStatus::Success) {
+            // Extract metadata from control NCA (Needed for version string)
+            if (auto romfs = control->GetRomFS()) {
+                if (auto extracted = ExtractRomFS(romfs)) {
+                    auto nacp_file = extracted->GetFile("control.nacp");
+                    if (!nacp_file) {
+                        nacp_file = extracted->GetFile("Control.nacp");
+                    }
+
+                    if (nacp_file) {
+                        NACP nacp(nacp_file);
+                        metadata.version = nacp.GetVersionString();
+                    }
+
+                }
+            }
+        }
+
+        if (is_update) {
+            const u64 base_program_id = program_id & ~0x800ULL;
+            update_to_base_map[program_id] = base_program_id;
+            LOG_INFO(Loader, "Identified external NSP as update: {} ({}) for base game {:016X}",
+                    path, metadata.version, base_program_id);
+        } else if (is_dlc) {
+            const u64 possible_base_id = GetBaseTitleID(program_id);
+            dlc_to_base_map[program_id] = possible_base_id;
+            LOG_INFO(Loader, "Identified external NSP as DLC: {} ({}) for base game {:016X}",
+                    path, metadata.version, possible_base_id);
+        } else {
+            LOG_WARNING(Loader, "External NSP {} does not match update or DLC criteria", path);
+        }
+    } catch (const std::exception& e) {
+        LOG_ERROR(Loader, "Error processing external file: {}", e.what());
+    }
+
+    file_metadata[program_id] = metadata;
+}
+
+ExternalFileMetadata ExternalContentManager::GetExternalUpdateMetadata(u64 program_id) const {
+    std::lock_guard lock{mutex};
+
+    for (const auto& [update_id, base_id] : update_to_base_map) {
+        if (base_id == program_id) {
+            auto it = file_metadata.find(update_id);
+            if (it != file_metadata.end()) {
+                return it->second;
+            }
+        }
+    }
+
+    // Placeholder empty metadata
+    ExternalFileMetadata empty_metadata;
+    empty_metadata.name = "";
+    empty_metadata.version = "";
+    return empty_metadata;
+}
+
+ExternalFileMetadata ExternalContentManager::GetExternalDLCMetadata(u64 program_id) const {
+    std::lock_guard lock{mutex};
+    ExternalFileMetadata metadata;
+    metadata.name = "DLC (File)";
+
+    std::string version_list;
+    bool first = true;
+
+    for (const auto& [dlc_id, base_id] : dlc_to_base_map) {
+        if (base_id == program_id) {
+            if (!first) {
+                version_list += ", ";
+            }
+
+            version_list += fmt::format("{}", dlc_id & 0x7FF);
+            first = false;
+
+            auto it = file_metadata.find(dlc_id);
+            if (it != file_metadata.end() && !metadata.name.empty()) {
+                metadata.name = it->second.name;
+            }
+        }
+    }
+
+    metadata.version = version_list.empty() ? "Unknown" : version_list;
+    return metadata;
+}
+
+u64 ExternalContentManager::ExtractProgramIDFromFile(const std::shared_ptr<NSP>& nsp) {
+    u64 id = nsp->GetProgramTitleID();
+    if (id != 0) return id;
+
+    LOG_WARNING(Loader, "NSP returned zero program ID, attempting fallback methods");
+
+    // Check all title IDs
+    const auto all_ids = nsp->GetProgramTitleIDs();
+    for (const auto& title_id : all_ids) {
+        if (title_id != 0) {
+            LOG_INFO(Loader, "Found ID from title IDs list: {:016X}", title_id);
+            return title_id;
+        }
+    }
+
+    // If all else fails, check the NCA files
+    const auto ncas = nsp->GetNCAsCollapsed();
+    for (const auto& nca : ncas) {
+        const auto nca_id = nca->GetTitleId();
+        if (nca_id != 0) {
+            LOG_INFO(Loader, "Found ID from NCA: {:016X}", nca_id);
+            return nca_id;
+        }
+    }
+
+    LOG_CRITICAL(Loader, "All ID detection methods failed, defaulting to 0");
+    return 0;
+}
+
+std::shared_ptr<ExternalContentManager> GetExternalContentManager() {
+    static std::shared_ptr<ExternalContentManager> instance =
+        std::make_shared<ExternalContentManager>();
+    return instance;
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/external_content_manager.h b/src/core/file_sys/external_content_manager.h
new file mode 100644
index 0000000000..b28e16f89b
--- /dev/null
+++ b/src/core/file_sys/external_content_manager.h
@@ -0,0 +1,191 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+#include <set>
+
+#include "common/common_types.h"
+#include "core/file_sys/submission_package.h"
+#include "core/file_sys/vfs/vfs.h"
+
+namespace FileSys {
+class ContentProvider;
+class NSP;
+} // namespace FileSys
+
+namespace FileSys {
+
+class VfsFile;
+class VfsFilesystem;
+
+/**
+ * @brief Struct that holds metadata for an external content file).
+ */
+struct ExternalFileMetadata {
+    std::string name;
+    std::string version;
+};
+
+
+/**
+ * @brief  The type of external content.
+ */
+enum class ExternalContentType {
+    Update,
+    DLC
+};
+
+/**
+ * @brief Manages external NSP addon files that are not installed to the virtual NAND.
+ *
+ */
+class ExternalContentManager {
+public:
+    explicit ExternalContentManager();
+    ~ExternalContentManager();
+
+    /**
+     * @brief Registers an external NSP file with the manager.
+     * @param path The literal path to the NSP file.
+     * @param program_id The program ID to associate with this file. If 0, the ID is read from the NSP.
+     * @return true if registration was successful, false otherwise (e.g., file not found, parse error).
+     */
+    bool RequestRegisterExternalNSP(const std::string& path, u64 program_id = 0);
+
+
+    /**
+     * @brief Checks if an external update is registered for the given base game ID.
+     * @param program_id The base program ID of the game.
+     */
+    bool HasExternalUpdate(u64 program_id) const;
+
+    /**
+     * @brief Checks if any external DLC is registered for the given base game ID.
+     * @param program_id The base program ID of the game.
+     */
+    bool HasExternalDLC(u64 program_id) const;
+
+    /**
+     * @brief Gets the VFS file object for a registered external file for a given base game ID.
+     * @param program_id The base program ID of the game.
+     * @param content_type The type of external content (update or DLC).
+     * @param first_only If true, only the first file is returned. If false, all files are returned.
+     * @return A vector of shared pointers to VfsFile objects. Empty if no files found.
+     */
+    std::vector<std::shared_ptr<VfsFile>> GetExternalFiles(u64 program_id, ExternalContentType content_type,
+                                                           bool first_only) const;
+
+    /**
+     * @brief Gets the VFS file object for the registered external update for a given base game ID.
+     * @param program_id The base program ID of the game.
+     * @return A shared pointer to the VfsFile if an update is found, nullptr otherwise.
+     */
+    std::shared_ptr<VfsFile> GetExternalUpdateFile(u64 program_id) const;
+
+    /**
+     * @brief Gets a list of VFS file objects for all registered external DLC for a given base game ID.
+     * @param program_id The base program ID of the game.
+     * @return A vector of shared pointers to VfsFile objects for the DLC. Empty if no DLC found.
+     */
+    std::vector<std::shared_ptr<VfsFile>> GetExternalDLCFiles(u64 program_id) const;
+
+    /**
+     * @brief Gets the extracted metadata for the registered external file for a given base game ID.
+     * @param program_id The base program ID of the game.
+     * @return An ExternalFileMetadata struct. Contains default values if no update or metadata is found.
+     */
+    ExternalFileMetadata GetExternalUpdateMetadata(u64 program_id) const;
+
+    /**
+     * @brief Get metadata for DLC files associated with the given base program ID.
+     * @param program_id The base program ID of the game.
+     * @return An ExternalFileMetadata struct. Contains default values if no DLC or metadata is found.
+     */
+    ExternalFileMetadata GetExternalDLCMetadata(u64 program_id) const;
+
+    /**
+     * @brief Extracts the program ID from an NSP file using multiple fallback methods.
+     * @param nsp The NSP file to extract the program ID from.
+     * @return The extracted program ID, or 0 if extraction failed.
+     */
+    static u64 ExtractProgramIDFromFile(const std::shared_ptr<NSP> &nsp);
+
+    /**
+     * @brief Loads the list of registered external NSP paths from a configuration file.
+     * Clears existing state before loading. Validates paths and attempts re-registration.
+     * @param config_path Path to the configuration file.
+     */
+    void LoadRegisteredPaths(const std::string& config_path);
+
+    /**
+     * @brief Saves the list of currently registered and existing external NSP paths to a configuration file.
+     * @param config_path Path to the configuration file.
+     */
+    void SaveRegisteredPaths(const std::string& config_path) const;
+
+    /**
+     * @brief Gets the set of all currently registered external NSP file paths.
+     * @return A copy of the set containing absolute paths.
+     */
+    std::set<std::string> GetAllRegisteredPaths() const;
+
+    /**
+   * @brief Gets the mapping of external DLC IDs to their base game IDs.
+   * @return A const reference to the unordered map of external DLC IDs to base game IDs.
+   */
+    const std::unordered_map<u64, u64>& GetExternalDLCMapping() const {
+        return dlc_to_base_map;
+    }
+
+    /**
+     * @brief Gets the VFS file object for a registered external DLC by its title ID.
+     * @param title_id The title ID of the DLC.
+     */
+    std::shared_ptr<VfsFile> GetExternalDLCFileByID(u64 title_id) const {
+        std::lock_guard lock{mutex};
+          auto it = external_files.find(title_id);
+        if (it != external_files.end() && !it->second.empty()) {
+            return it->second.front().second;
+        }
+          return nullptr;
+    }
+
+private:
+    /**
+     * @brief Internal, non-locking version of NSP registration logic.
+     * Assumes caller holds the mutex.
+     */
+    bool RegisterExternalNSP(const std::string& path, u64 program_id);
+
+    /**
+     * @brief Parses a registered file to determine if it's an update or DLC and populates internal maps.
+     * Also triggers metadata extraction and storage.
+     */
+    void ParseExternalFile(const std::string& path, u64 program_id);
+
+    template <typename MapType>
+    bool CheckMapForBaseID(const MapType& map, u64 program_id) const;
+
+    std::shared_ptr<VfsFilesystem> vfs;
+    std::set<std::string> registered_paths;
+    std::unordered_map<u64, std::vector<std::pair<std::string, std::shared_ptr<VfsFile>>>> external_files;
+    std::unordered_map<u64, u64> update_to_base_map;
+    std::unordered_map<u64, u64> dlc_to_base_map;
+    std::unordered_map<u64, ExternalFileMetadata> file_metadata;
+    mutable std::mutex mutex;
+};
+
+/**
+ * @brief Gets the instance of the ExternalContentManager.
+ * @return A shared pointer to the ExternalContentManager instance.
+ */
+std::shared_ptr<ExternalContentManager> GetExternalContentManager();
+
+} // namespace FileSys
\ No newline at end of file
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 21d45235e4..83cb8f688f 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -1,6 +1,9 @@
 // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
 #include <algorithm>
 #include <array>
 #include <cstddef>
@@ -30,6 +33,7 @@
 #include "core/loader/loader.h"
 #include "core/loader/nso.h"
 #include "core/memory/cheat_engine.h"
+#include "core/file_sys/external_content_manager.h"
 
 namespace FileSys {
 namespace {
@@ -117,9 +121,10 @@ bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
 } // Anonymous namespace
 
 PatchManager::PatchManager(u64 title_id_,
-                           const Service::FileSystem::FileSystemController& fs_controller_,
-                           const ContentProvider& content_provider_)
-    : title_id{title_id_}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
+                         const Service::FileSystem::FileSystemController& fs_controller_,
+                         const ContentProvider& content_provider_)
+    : title_id{title_id_}, fs_controller{fs_controller_}, content_provider{content_provider_},
+      external_manager{GetExternalContentManager()} {}
 
 PatchManager::~PatchManager() = default;
 
@@ -128,30 +133,43 @@ u64 PatchManager::GetTitleID() const {
 }
 
 VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
-    LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
-
-    if (exefs == nullptr)
+    if (!exefs)
         return exefs;
 
-    const auto& disabled = Settings::values.disabled_addons[title_id];
-    const auto update_disabled =
-        std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
+    // Retrieve base Program NCA
+    const auto base_program_nca = content_provider.GetEntry(title_id, ContentRecordType::Program);
+    const bool update_disabled = IsAddOnDisabled("Update", false);
+    const bool external_update_disabled = IsAddOnDisabled("Update (File):", true);
+    bool external_update_applied = false;
 
-    // Game Updates
+    // External Game Updates
+    // TODO: Add a setting for users to select update priority (i.e if external update should be used over NAND)
+    if (!external_update_disabled &&
+        external_manager && external_manager->HasExternalUpdate(title_id) && base_program_nca) {
+
+        const auto update_file = external_manager->GetExternalUpdateFile(title_id);
+        if (update_file) {
+            auto external_exefs = PatchExeFSWithExternal(exefs, base_program_nca.get(), update_file);
+            if (external_exefs) {
+                const auto metadata = external_manager->GetExternalUpdateMetadata(title_id);
+                exefs = external_exefs;
+                external_update_applied = true;
+            }
+        }
+    }
+
+    // NAND Game Updates
     const auto update_tid = GetUpdateTitleID(title_id);
     const auto update = content_provider.GetEntry(update_tid, ContentRecordType::Program);
-
-    if (!update_disabled && update != nullptr && update->GetExeFS() != nullptr) {
-        LOG_INFO(Loader, "    ExeFS: Update ({}) applied successfully",
-                 FormatTitleVersion(content_provider.GetEntryVersion(update_tid).value_or(0)));
+    if (!external_update_applied && !update_disabled && update && update->GetExeFS()) {
         exefs = update->GetExeFS();
     }
 
-    // LayeredExeFS
     const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
     const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
 
-    std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
+    std::vector<VirtualDir> layers;
+    std::vector patch_dirs = {sdmc_load_dir};
     if (load_dir != nullptr) {
         const auto load_patch_dirs = load_dir->GetSubdirectories();
         patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
@@ -160,8 +178,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
     std::sort(patch_dirs.begin(), patch_dirs.end(),
               [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
 
-    std::vector<VirtualDir> layers;
-    layers.reserve(patch_dirs.size() + 1);
+    const auto& disabled = Settings::values.disabled_addons[title_id];
     for (const auto& subdir : patch_dirs) {
         if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
             continue;
@@ -190,6 +207,23 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
     return exefs;
 }
 
+VirtualDir PatchManager::PatchExeFSWithExternal(VirtualDir exefs, const NCA* base_nca,
+                                                VirtualFile external_update) const {
+    if (!external_update || !base_nca) {
+        return nullptr;
+    }
+
+    auto new_nca = GetNCAfromExternalFile(external_update, ContentRecordType::Program, base_nca);
+    if (new_nca && new_nca->GetStatus() == Loader::ResultStatus::Success) {
+        const auto external_exefs = new_nca->GetExeFS();
+        if (external_exefs) {
+            return external_exefs;
+        }
+    }
+
+    return nullptr;
+}
+
 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];
@@ -431,15 +465,32 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
 
     auto romfs = base_romfs;
 
+    // External updates
+    // TODO: Add a setting for users to select update priority (I.E if external update should be used over NAND)
+    const bool update_disabled = IsAddOnDisabled("Update", false);
+    const bool external_update_disabled = IsAddOnDisabled("Update (File):", true);
+    bool external_update_applied = false;
+
+    if (!external_update_disabled &&
+        external_manager && external_manager->HasExternalUpdate(title_id)) {
+
+        const auto update_file = external_manager->GetExternalUpdateFile(title_id);
+        if (update_file && base_nca) {
+            auto patched = PatchRomFSWithExternal(base_nca, romfs, update_file, type);
+            if (patched) {
+                const auto metadata = external_manager->GetExternalUpdateMetadata(title_id);
+                LOG_INFO(Loader, "Applied external update RomFS (v{})", metadata.version);
+                romfs = patched;
+                external_update_applied = true;
+            }
+        }
+    }
+
     // Game Updates
     const auto update_tid = GetUpdateTitleID(title_id);
     const auto update_raw = content_provider.GetEntryRaw(update_tid, type);
 
-    const auto& disabled = Settings::values.disabled_addons[title_id];
-    const auto update_disabled =
-        std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
-
-    if (!update_disabled && update_raw != nullptr && base_nca != nullptr) {
+    if (!external_update_applied && !update_disabled && update_raw && base_nca) {
         const auto new_nca = std::make_shared<NCA>(update_raw, base_nca);
         if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
             new_nca->GetRomFS() != nullptr) {
@@ -449,7 +500,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
             const auto version =
                 FormatTitleVersion(content_provider.GetEntryVersion(update_tid).value_or(0));
         }
-    } else if (!update_disabled && packed_update_raw != nullptr && base_nca != nullptr) {
+    } else if (!external_update_applied && !update_disabled && packed_update_raw != nullptr && base_nca != nullptr) {
         const auto new_nca = std::make_shared<NCA>(packed_update_raw, base_nca);
         if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
             new_nca->GetRomFS() != nullptr) {
@@ -466,6 +517,23 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
     return romfs;
 }
 
+VirtualFile PatchManager::PatchRomFSWithExternal(const NCA* base_nca, VirtualFile base_romfs,
+                                               VirtualFile external_update, ContentRecordType type) const {
+    if (!external_update || !base_nca) {
+        return nullptr;
+    }
+
+    auto new_nca = GetNCAfromExternalFile(external_update, type, base_nca);
+    if (new_nca && new_nca->GetStatus() == Loader::ResultStatus::Success) {
+        const auto update_romfs = new_nca->GetRomFS();
+        if (update_romfs) {
+            return update_romfs;
+        }
+    }
+
+    return nullptr;
+}
+
 std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
     if (title_id == 0) {
         return {};
@@ -480,8 +548,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
     const auto metadata = update.GetControlMetadata();
     const auto& nacp = metadata.first;
 
-    const auto update_disabled =
-        std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
+    const auto update_disabled = IsAddOnDisabled("Update", false);
     Patch update_patch = {.enabled = !update_disabled,
                           .name = "Update",
                           .version = "",
@@ -507,6 +574,21 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
         }
     }
 
+    // External Updates
+    const auto external_update_disabled = IsAddOnDisabled("Update (File):", true);
+    if (external_manager->HasExternalUpdate(title_id)) {
+        // Get metadata for the external update
+        const auto target_metadata = external_manager->GetExternalUpdateMetadata(title_id);
+
+        Patch external_patch = {.enabled = !external_update_disabled,
+                               .name = target_metadata.name,
+                               .version = target_metadata.version,
+                               .type = PatchType::Update,
+                               .program_id = title_id,
+                               .title_id = title_id};
+        out.push_back(external_patch);
+    }
+
     // General Mods (LayeredFS and IPS)
     const auto mod_dir = fs_controller.GetModificationLoadRoot(title_id);
     if (mod_dir != nullptr) {
@@ -610,10 +692,34 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
                        .title_id = dlc_match.back().title_id});
     }
 
+    // External DLC
+    const auto external_dlc_disabled = IsAddOnDisabled("DLC (File):", true);
+    if (external_manager->HasExternalDLC(title_id)) {
+        const auto target_metadata = external_manager->GetExternalDLCMetadata(title_id);
+
+        out.push_back({.enabled = !external_dlc_disabled,
+                      .name = target_metadata.name,
+                      .version = target_metadata.version,
+                      .type = PatchType::DLC,
+                      .program_id = title_id,
+                      .title_id = title_id});
+    }
+
     return out;
 }
 
 std::optional<u32> PatchManager::GetGameVersion() const {
+    // Prioritize external update version
+    const auto external_update_disabled = IsAddOnDisabled("Update (File):", true);
+    if (!external_update_disabled && external_manager &&
+        external_manager->HasExternalUpdate(title_id)) {
+        const auto metadata = external_manager->GetExternalUpdateMetadata(title_id);
+        if (metadata.version != "") {
+            return std::stoul(metadata.version);
+        }
+    }
+
+    // Then check NAND installed version
     const auto update_tid = GetUpdateTitleID(title_id);
     if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
         return content_provider.GetEntryVersion(update_tid);
@@ -693,4 +799,79 @@ PatchManager::Metadata PatchManager::ParseControlNCA(const NCA& nca) const {
 
     return {std::move(nacp), icon_file};
 }
-} // namespace FileSys
+
+bool PatchManager::IsAddOnDisabled(const std::string& feature_name, bool check_prefix) const {
+    const auto it = Settings::values.disabled_addons.find(title_id);
+    if (it == Settings::values.disabled_addons.end()) {
+        return false;
+    }
+
+    const auto& disabled = it->second;
+
+    if (check_prefix) {
+        return std::find_if(disabled.cbegin(), disabled.cend(),
+                      [&feature_name](const std::string& name) {
+                          return name.starts_with(feature_name);
+                      }) != disabled.cend();
+    }
+
+    return std::find(disabled.cbegin(), disabled.cend(), feature_name) != disabled.cend();
+}
+
+std::shared_ptr<NCA> PatchManager::GetNCAfromExternalFile(VirtualFile update_file,
+                                                      ContentRecordType type,
+                                                      const NCA* base_nca) const {
+    if (!update_file || !base_nca) {
+        return nullptr;
+    }
+
+    try {
+        auto nsp = std::make_shared<NSP>(update_file);
+        if (nsp->GetStatus() != Loader::ResultStatus::Success) {
+            LOG_WARNING(Loader, "Failed to parse NSP file");
+            return nullptr;
+        }
+
+        const auto update_id = title_id | 0x800;
+        auto nca = nsp->GetNCA(update_id, type, TitleType::Update);
+
+        if (!nca || nca->GetStatus() != Loader::ResultStatus::Success) {
+            LOG_WARNING(Loader, "Direct NCA lookup failed, searching all NCAs for type match");
+
+            for (const auto& candidate : nsp->GetNCAsCollapsed()) {
+                                bool type_matches = false;
+                switch (candidate->GetType()) {
+                    case NCAContentType::Program:
+                        type_matches = (type == ContentRecordType::Program);
+                        break;
+                    case NCAContentType::Control:
+                        type_matches = (type == ContentRecordType::Control);
+                        break;
+                    case NCAContentType::Data:
+                        type_matches = (type == ContentRecordType::Data);
+                        break;
+                    default:
+                        type_matches = false;
+                        break;
+                }
+
+                if (type_matches) {
+                    nca = candidate;
+                    break;
+                }
+            }
+        }
+
+        if (nca && nca->GetStatus() == Loader::ResultStatus::Success) {
+            return std::make_shared<NCA>(nca->GetBaseFile(), base_nca);
+        }
+
+        LOG_WARNING(Loader, "No suitable NCA found in external file");
+    } catch (const std::exception& e) {
+        LOG_ERROR(Loader, "Error processing external file: {}", e.what());
+    }
+
+    return nullptr;
+}
+
+} // namespace FileSys
\ No newline at end of file
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index 552c0fbe23..0b53d835a6 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -1,6 +1,9 @@
 // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
 #pragma once
 
 #include <map>
@@ -21,7 +24,7 @@ class FileSystemController;
 }
 
 namespace FileSys {
-
+class ExternalContentManager;
 class ContentProvider;
 class NCA;
 class NACP;
@@ -76,6 +79,37 @@ public:
                                          VirtualFile packed_update_raw = nullptr,
                                          bool apply_layeredfs = true) const;
 
+    /**
+     * Applies an external update file to patch a game's RomFS.
+     * @param base_nca The base NCA of the content being patched
+     * @param base_romfs The original RomFS to be patched
+     * @param external_update The external update file (NSP/XCI) containing the update
+     * @param type The type of content being patched (Program, Control, etc.)
+     * @return Patched RomFS VirtualFile if successful, nullptr otherwise
+     */
+    VirtualFile PatchRomFSWithExternal(const NCA *base_nca, VirtualFile base_romfs,
+                                       VirtualFile external_update, ContentRecordType type) const;
+
+    /**
+     * Applies an external update file to patch a game's ExeFS.
+     * @param exefs The original ExeFS to be patched
+     * @param base_nca The base NCA of the content being patched
+     * @param external_update The external update file (NSP/XCI) containing the update
+     * @return Patched ExeFS VirtualDir if successful, nullptr otherwise
+     */
+    VirtualDir PatchExeFSWithExternal(VirtualDir exefs, const NCA *base_nca, VirtualFile external_update) const;
+
+
+    /**
+     *  Extract an NCA of the specified type from an external update file.
+     * @param update_file The external update file (NSP/XCI)
+     * @param type The type of NCA to extract (Program, Control, etc.)
+     * @param base_nca The base NCA to use as a template for the update NCA
+     * @return Shared pointer to the extracted and templated NCA if successful, nullptr otherwise
+     */
+    std::shared_ptr<NCA> GetNCAfromExternalFile(VirtualFile update_file, ContentRecordType type,
+                                                const NCA *base_nca) const;
+
     // Returns a vector of patches
     [[nodiscard]] std::vector<Patch> GetPatches(VirtualFile update_raw = nullptr) const;
 
@@ -95,9 +129,18 @@ private:
     [[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
                                                           const std::string& build_id) const;
 
+    /**
+     * Checks if a specific feature is disabled in user settings for this title.
+     * @param feature_name The string name of the addon to check against
+     * @param check_prefix If true, checks if the addon name starts with the given string else it's a literal check
+     */
+    bool IsAddOnDisabled(const std::string &feature_name, bool check_prefix) const;
+
+
     u64 title_id;
     const Service::FileSystem::FileSystemController& fs_controller;
     const ContentProvider& content_provider;
+    std::shared_ptr<ExternalContentManager> external_manager;
 };
 
 } // namespace FileSys
diff --git a/src/core/hle/service/am/process_creation.cpp b/src/core/hle/service/am/process_creation.cpp
index aaa03c4c39..7154c3f116 100644
--- a/src/core/hle/service/am/process_creation.cpp
+++ b/src/core/hle/service/am/process_creation.cpp
@@ -1,6 +1,9 @@
 // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
 #include "core/core.h"
 #include "core/file_sys/content_archive.h"
 #include "core/file_sys/nca_metadata.h"
@@ -8,6 +11,7 @@
 #include "core/file_sys/registered_cache.h"
 #include "core/file_sys/romfs_factory.h"
 #include "core/hle/service/am/process_creation.h"
+#include "core/file_sys/external_content_manager.h"
 #include "core/hle/service/glue/glue_manager.h"
 #include "core/hle/service/os/process.h"
 #include "core/loader/loader.h"
@@ -97,18 +101,32 @@ std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control,
     }
 
     FileSys::NACP nacp;
-    if (out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
-        out_control = nacp.GetRawBytes();
-    } else {
-        out_control.resize(sizeof(FileSys::RawNACP));
-        std::fill(out_control.begin(), out_control.end(), (u8) 0);
+    bool nacp_loaded = false;
+    auto& storage = system.GetContentProviderUnion();
+    FileSys::PatchManager pm{program_id, system.GetFileSystemController(), storage};
+
+
+    const auto metadata = pm.GetControlMetadata();
+    if (metadata.first) {
+        out_control = metadata.first->GetRawBytes();
+        nacp_loaded = true;
+    }
+
+    //  Fall back to the loader's NACP if no metadata was found through PatchManager
+    if (!nacp_loaded && out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
+        out_control = nacp.GetRawBytes();
+        nacp_loaded = true;
+    }
+
+    if (!nacp_loaded) {
+        LOG_WARNING(Service_AM, "Failed to load NACP data, filling with zeros.");
+        out_control.resize(sizeof(FileSys::RawNACP));
+        std::fill(out_control.begin(), out_control.end(), u8{0});
     }
 
-    auto& storage = system.GetContentProviderUnion();
     Service::Glue::ApplicationLaunchProperty launch{};
     launch.title_id = process->GetProgramId();
 
-    FileSys::PatchManager pm{launch.title_id, system.GetFileSystemController(), storage};
     launch.version = pm.GetGameVersion().value_or(0);
 
     // TODO(DarkLordZach): When FSController/Game Card Support is added, if
diff --git a/src/core/hle/service/aoc/addon_content_manager.cpp b/src/core/hle/service/aoc/addon_content_manager.cpp
index d47f57d645..cfd288e600 100644
--- a/src/core/hle/service/aoc/addon_content_manager.cpp
+++ b/src/core/hle/service/aoc/addon_content_manager.cpp
@@ -1,6 +1,10 @@
 // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+
 #include <algorithm>
 #include <numeric>
 #include <vector>
@@ -16,6 +20,7 @@
 #include "core/file_sys/registered_cache.h"
 #include "core/hle/kernel/k_event.h"
 #include "core/hle/service/aoc/addon_content_manager.h"
+#include "core/file_sys/external_content_manager.h"
 #include "core/hle/service/aoc/purchase_event_manager.h"
 #include "core/hle/service/cmif_serialization.h"
 #include "core/hle/service/ipc_helpers.h"
@@ -30,6 +35,8 @@ static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
 
 static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) {
     std::vector<u64> add_on_content;
+
+    // Nand DLC
     const auto& rcu = system.GetContentProvider();
     const auto list =
         rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
@@ -43,6 +50,25 @@ static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) {
                        Loader::ResultStatus::Success;
             }),
         add_on_content.end());
+
+    // External DLC
+    if (auto external_manager = FileSys::GetExternalContentManager()) {
+        const auto current_app_id = system.GetApplicationProcessProgramID();
+        const auto base_id = FileSys::GetBaseTitleID(current_app_id);
+
+        const auto& disabled = Settings::values.disabled_addons[base_id];
+        const bool external_dlc_disabled = std::find_if(disabled.cbegin(), disabled.cend(),
+                  [](const std::string& name) { return name.starts_with("DLC (File):"); }) != disabled.cend();
+
+        if (!external_dlc_disabled) {
+            for (const auto& [dlc_id, mapped_base_id] : external_manager->GetExternalDLCMapping()) {
+                if (mapped_base_id == base_id) {
+                    add_on_content.push_back(dlc_id);
+                }
+            }
+        }
+    }
+
     return add_on_content;
 }
 
@@ -91,7 +117,8 @@ Result IAddOnContentManager::CountAddOnContent(Out<u32> out_count, ClientProcess
     const auto current = system.GetApplicationProcessProgramID();
 
     const auto& disabled = Settings::values.disabled_addons[current];
-    if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
+    if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end() ||
+        std::find(disabled.begin(), disabled.end(), "DLC (File):") != disabled.end()) {
         *out_count = 0;
         R_SUCCEED();
     }
@@ -113,7 +140,8 @@ Result IAddOnContentManager::ListAddOnContent(Out<u32> out_count,
 
     std::vector<u32> out;
     const auto& disabled = Settings::values.disabled_addons[current];
-    if (std::find(disabled.begin(), disabled.end(), "DLC") == disabled.end()) {
+    if (std::find(disabled.begin(), disabled.end(), "DLC") == disabled.end() ||
+        std::find(disabled.begin(), disabled.end(), "DLC (File):") == disabled.end()) {
         for (u64 content_id : add_on_content) {
             if (FileSys::GetBaseTitleID(content_id) != current) {
                 continue;
diff --git a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp
index 005caf6562..6b4e2ff904 100644
--- a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp
@@ -1,6 +1,9 @@
 // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
 #include <cinttypes>
 #include <cstring>
 #include <iterator>
@@ -34,6 +37,8 @@
 #include "core/hle/service/filesystem/fsp/fs_i_save_data_info_reader.h"
 #include "core/hle/service/filesystem/fsp/fs_i_storage.h"
 #include "core/hle/service/filesystem/fsp/fsp_srv.h"
+
+#include "core/file_sys/external_content_manager.h"
 #include "core/hle/service/filesystem/fsp/save_data_transfer_prohibiter.h"
 #include "core/hle/service/filesystem/romfs_controller.h"
 #include "core/hle/service/filesystem/save_data_controller.h"
@@ -41,6 +46,8 @@
 #include "core/hle/service/ipc_helpers.h"
 #include "core/loader/loader.h"
 #include "core/reporter.h"
+#include "core/file_sys/common_funcs.h"
+#include "core/file_sys/submission_package.h"
 
 namespace Service::FileSystem {
 
@@ -426,6 +433,10 @@ Result FSP_SRV::OpenDataStorageByDataId(OutInterface<IStorage> out_interface,
     LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}",
               storage_id, unknown, title_id);
 
+    if (TryOpenExternalDLC(out_interface, title_id)) {
+        R_SUCCEED();
+    }
+
     auto data = romfs_controller->OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
 
     if (!data) {
@@ -454,6 +465,54 @@ Result FSP_SRV::OpenDataStorageByDataId(OutInterface<IStorage> out_interface,
     R_SUCCEED();
 }
 
+bool FSP_SRV::TryOpenExternalDLC(OutInterface<IStorage>& out_interface, u64 title_id) const {
+    auto external_manager = FileSys::GetExternalContentManager();
+    if (!external_manager) {
+        return false;
+    }
+
+    const auto base_title_id = FileSys::GetBaseTitleID(title_id);
+        if (!external_manager->HasExternalDLC(base_title_id)) {
+        return false;
+    }
+
+    auto dlc_file = external_manager->GetExternalDLCFileByID(title_id);
+    if (!dlc_file) {
+        return false;
+    }
+
+    const auto nsp = std::make_shared<FileSys::NSP>(dlc_file);
+    if (nsp->GetStatus() != Loader::ResultStatus::Success) {
+        LOG_ERROR(Service_FS, "NSP creation failed for DLC title_id={:016X}", title_id);
+        return false;
+    }
+
+    std::shared_ptr<FileSys::NCA> data_nca = nullptr;
+
+    for (const auto& nca : nsp->GetNCAsCollapsed()) {
+        if (nca->GetType() == FileSys::NCAContentType::Data ||
+            nca->GetType() == FileSys::NCAContentType::PublicData) {
+            data_nca = nca;
+            break;
+        }
+    }
+
+    if (!data_nca || data_nca->GetStatus() != Loader::ResultStatus::Success) {
+        LOG_ERROR(Service_FS, "Could not find Data NCA in NSP for title_id={:016X}", title_id);
+        return false;
+    }
+
+    auto dlc_romfs = data_nca->GetRomFS();
+    if (!dlc_romfs) {
+        LOG_ERROR(Service_FS, "Data NCA exists but has no RomFS for title_id={:016X}", title_id);
+        return false;
+    }
+
+    *out_interface = std::make_shared<IStorage>(system, dlc_romfs);
+    LOG_INFO(Service_FS, "Successfully opened external DLC for title_id={:016X}", title_id);
+    return true;
+}
+
 Result FSP_SRV::OpenPatchDataStorageByCurrentProcess(OutInterface<IStorage> out_interface,
                                                      FileSys::StorageId storage_id, u64 title_id) {
     LOG_WARNING(Service_FS, "(STUBBED) called with storage_id={:02X}, title_id={:016X}", storage_id,
diff --git a/src/core/hle/service/filesystem/fsp/fsp_srv.h b/src/core/hle/service/filesystem/fsp/fsp_srv.h
index cd9d66b7ba..b1cfb7d867 100644
--- a/src/core/hle/service/filesystem/fsp/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp/fsp_srv.h
@@ -1,6 +1,9 @@
 // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
 #pragma once
 
 #include <memory>
@@ -112,6 +115,8 @@ private:
     const FileSys::ContentProvider& content_provider;
     const Core::Reporter& reporter;
 
+    bool TryOpenExternalDLC(OutInterface<IStorage>& out_interface, u64 title_id) const;
+
     FileSys::VirtualFile romfs;
     u64 current_process_id = 0;
     u32 access_log_program_index = 0;