diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 30eacd803b6a9212dc87c5f1404a91bb124b6888..01f984098e14dafeacedf65dd631d8fa6e8d2d7b 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -259,6 +259,15 @@ public:
         return ResultStatus::ErrorNotImplemented;
     }
 
+    /**
+     * Get the RomFS of the manual of the application
+     * @param file The raw manual RomFS of the game
+     * @return ResultStatus result of function
+     */
+    virtual ResultStatus ReadManualRomFS(FileSys::VirtualFile& file) {
+        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 4d4b44571d5613f42a05dd9faf5ae48857f41b1a..7fcb12aa222de5e14a9a4d8b14c73ad6e8791802 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -158,4 +158,12 @@ ResultStatus AppLoader_NSP::ReadControlData(FileSys::NACP& nacp) {
     nacp = *nacp_file;
     return ResultStatus::Success;
 }
+
+ResultStatus AppLoader_NSP::ReadManualRomFS(FileSys::VirtualFile& file) {
+    const auto nca = nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Manual);
+    if (nsp->GetStatus() != ResultStatus::Success || nca == nullptr)
+        return ResultStatus::ErrorNoRomFS;
+    file = nca->GetRomFS();
+    return file == nullptr ? ResultStatus::ErrorNoRomFS : ResultStatus::Success;
+}
 } // namespace Loader
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index 32eb0193d285530edf05e263709e542f55f85aa8..0841578d458a48ba367ec7e8c3bc42a2f1d71338 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -44,6 +44,8 @@ public:
     ResultStatus ReadIcon(std::vector<u8>& buffer) override;
     ResultStatus ReadTitle(std::string& title) override;
     ResultStatus ReadControlData(FileSys::NACP& nacp) override;
+    ResultStatus ReadDeveloper(std::string& developer) override;
+    ResultStatus ReadManualRomFS(FileSys::VirtualFile& file) override;
 
 private:
     std::unique_ptr<FileSys::NSP> nsp;
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index e67e43c69d3cc89b5bca8ce82f10c2bc9e9f31ae..ff60a3756da7752777fdaab7ead9fde14a68e416 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -128,4 +128,13 @@ ResultStatus AppLoader_XCI::ReadControlData(FileSys::NACP& control) {
     return ResultStatus::Success;
 }
 
+ResultStatus AppLoader_XCI::ReadManualRomFS(FileSys::VirtualFile& file) {
+    const auto nca = xci->GetSecurePartitionNSP()->GetNCA(xci->GetProgramTitleID(),
+                                                          FileSys::ContentRecordType::Manual);
+    if (xci->GetStatus() != ResultStatus::Success || nca == nullptr)
+        return ResultStatus::ErrorXCIMissingPartition;
+    file = nca->GetRomFS();
+    return file == nullptr ? ResultStatus::ErrorNoRomFS : ResultStatus::Success;
+}
+
 } // namespace Loader
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 9d3923f62bdf1a3fa781f472e278f3bdd28fceeb..3e6e19a44f3587d0f2bf8825c1bef799755b1582 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -44,6 +44,8 @@ public:
     ResultStatus ReadIcon(std::vector<u8>& buffer) override;
     ResultStatus ReadTitle(std::string& title) override;
     ResultStatus ReadControlData(FileSys::NACP& control) override;
+    ResultStatus ReadDeveloper(std::string& developer) override;
+    ResultStatus ReadManualRomFS(FileSys::VirtualFile& file) override;
 
 private:
     std::unique_ptr<FileSys::XCI> xci;