Просмотр исходного кода

applets/web: Implement the offline browser applet backend

Morph 5 лет назад
Родитель
Сommit
89df483567

+ 133 - 13
src/core/hle/service/am/applets/web_browser.cpp

@@ -2,16 +2,26 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
-#include <optional>
-
 #include "common/assert.h"
+#include "common/common_paths.h"
+#include "common/file_util.h"
 #include "common/logging/log.h"
 #include "common/string_util.h"
 #include "core/core.h"
+#include "core/file_sys/content_archive.h"
+#include "core/file_sys/mode.h"
+#include "core/file_sys/nca_metadata.h"
+#include "core/file_sys/patch_manager.h"
+#include "core/file_sys/registered_cache.h"
+#include "core/file_sys/romfs.h"
+#include "core/file_sys/system_archive/system_archive.h"
+#include "core/file_sys/vfs_types.h"
 #include "core/frontend/applets/web_browser.h"
+#include "core/hle/kernel/process.h"
 #include "core/hle/result.h"
 #include "core/hle/service/am/am.h"
 #include "core/hle/service/am/applets/web_browser.h"
+#include "core/hle/service/filesystem/filesystem.h"
 
 namespace Service::AM::Applets {
 
@@ -36,6 +46,16 @@ std::string ParseStringValue(const std::vector<u8>& data) {
                                                        data.size());
 }
 
+std::string GetMainURL(const std::string& url) {
+    const auto index = url.find('?');
+
+    if (index == std::string::npos) {
+        return url;
+    }
+
+    return url.substr(0, index);
+}
+
 WebArgInputTLVMap ReadWebArgs(const std::vector<u8>& web_arg, WebArgHeader& web_arg_header) {
     std::memcpy(&web_arg_header, web_arg.data(), sizeof(WebArgHeader));
 
@@ -72,15 +92,35 @@ WebArgInputTLVMap ReadWebArgs(const std::vector<u8>& web_arg, WebArgHeader& web_
     return input_tlv_map;
 }
 
-std::optional<std::vector<u8>> GetInputTLVData(const WebArgInputTLVMap& input_tlv_map,
-                                               WebArgInputTLVType input_tlv_type) {
-    const auto map_it = input_tlv_map.find(input_tlv_type);
+FileSys::VirtualFile GetOfflineRomFS(Core::System& system, u64 title_id,
+                                     FileSys::ContentRecordType nca_type) {
+    if (nca_type == FileSys::ContentRecordType::Data) {
+        const auto nca =
+            system.GetFileSystemController().GetSystemNANDContents()->GetEntry(title_id, nca_type);
+
+        if (nca == nullptr) {
+            LOG_ERROR(Service_AM,
+                      "NCA of type={} with title_id={:016X} is not found in the System NAND!",
+                      nca_type, title_id);
+            return FileSys::SystemArchive::SynthesizeSystemArchive(title_id);
+        }
 
-    if (map_it == input_tlv_map.end()) {
-        return std::nullopt;
-    }
+        return nca->GetRomFS();
+    } else {
+        const auto nca = system.GetContentProvider().GetEntry(title_id, nca_type);
 
-    return map_it->second;
+        if (nca == nullptr) {
+            LOG_ERROR(Service_AM,
+                      "NCA of type={} with title_id={:016X} is not found in the ContentProvider!",
+                      nca_type, title_id);
+            return nullptr;
+        }
+
+        const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
+                                       system.GetContentProvider()};
+
+        return pm.PatchRomFS(nca->GetRomFS(), nca->GetBaseIVFCOffset(), nca_type);
+    }
 }
 
 } // namespace
@@ -209,11 +249,92 @@ void WebBrowser::WebBrowserExit(WebExitReason exit_reason, std::string last_url)
     broker.SignalStateChanged();
 }
 
+bool WebBrowser::InputTLVExistsInMap(WebArgInputTLVType input_tlv_type) const {
+    return web_arg_input_tlv_map.find(input_tlv_type) != web_arg_input_tlv_map.end();
+}
+
+std::optional<std::vector<u8>> WebBrowser::GetInputTLVData(WebArgInputTLVType input_tlv_type) {
+    const auto map_it = web_arg_input_tlv_map.find(input_tlv_type);
+
+    if (map_it == web_arg_input_tlv_map.end()) {
+        return std::nullopt;
+    }
+
+    return map_it->second;
+}
+
 void WebBrowser::InitializeShop() {}
 
 void WebBrowser::InitializeLogin() {}
 
-void WebBrowser::InitializeOffline() {}
+void WebBrowser::InitializeOffline() {
+    const auto document_path =
+        ParseStringValue(GetInputTLVData(WebArgInputTLVType::DocumentPath).value());
+
+    const auto document_kind =
+        ParseRawValue<DocumentKind>(GetInputTLVData(WebArgInputTLVType::DocumentKind).value());
+
+    u64 title_id{};
+    FileSys::ContentRecordType nca_type{FileSys::ContentRecordType::HtmlDocument};
+    std::string additional_paths;
+
+    switch (document_kind) {
+    case DocumentKind::OfflineHtmlPage:
+        title_id = system.CurrentProcess()->GetTitleID();
+        nca_type = FileSys::ContentRecordType::HtmlDocument;
+        additional_paths = "html-document";
+        break;
+    case DocumentKind::ApplicationLegalInformation:
+        title_id = ParseRawValue<u64>(GetInputTLVData(WebArgInputTLVType::ApplicationID).value());
+        nca_type = FileSys::ContentRecordType::LegalInformation;
+        break;
+    case DocumentKind::SystemDataPage:
+        title_id = ParseRawValue<u64>(GetInputTLVData(WebArgInputTLVType::SystemDataID).value());
+        nca_type = FileSys::ContentRecordType::Data;
+        break;
+    }
+
+    static constexpr std::array<const char*, 3> RESOURCE_TYPES{
+        "manual",
+        "legal_information",
+        "system_data",
+    };
+
+    offline_cache_dir = Common::FS::SanitizePath(
+        fmt::format("{}/offline_web_applet_{}/{:016X}",
+                    Common::FS::GetUserPath(Common::FS::UserPath::CacheDir),
+                    RESOURCE_TYPES[static_cast<u32>(document_kind) - 1], title_id),
+        Common::FS::DirectorySeparator::PlatformDefault);
+
+    offline_document = Common::FS::SanitizePath(
+        fmt::format("{}/{}/{}", offline_cache_dir, additional_paths, document_path),
+        Common::FS::DirectorySeparator::PlatformDefault);
+
+    const auto main_url = Common::FS::SanitizePath(GetMainURL(offline_document),
+                                                   Common::FS::DirectorySeparator::PlatformDefault);
+
+    if (Common::FS::Exists(main_url)) {
+        return;
+    }
+
+    auto offline_romfs = GetOfflineRomFS(system, title_id, nca_type);
+
+    if (offline_romfs == nullptr) {
+        LOG_ERROR(Service_AM, "RomFS with title_id={:016X} and nca_type={} cannot be extracted!",
+                  title_id, nca_type);
+        return;
+    }
+
+    LOG_DEBUG(Service_AM, "Extracting RomFS to {}", offline_cache_dir);
+
+    const auto extracted_romfs_dir =
+        FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard);
+
+    const auto temp_dir =
+        system.GetFilesystem()->CreateDirectory(offline_cache_dir, FileSys::Mode::ReadWrite);
+
+    FileSys::VfsRawCopyD(extracted_romfs_dir, temp_dir);
+}
 
 void WebBrowser::InitializeShare() {}
 
@@ -234,8 +355,8 @@ void WebBrowser::ExecuteLogin() {
 }
 
 void WebBrowser::ExecuteOffline() {
-    LOG_WARNING(Service_AM, "(STUBBED) called, Offline Applet is not implemented");
-    WebBrowserExit(WebExitReason::EndButtonPressed);
+    LOG_INFO(Service_AM, "Opening offline document at {}", offline_document);
+    WebBrowserExit(WebExitReason::WindowClosed);
 }
 
 void WebBrowser::ExecuteShare() {
@@ -257,5 +378,4 @@ void WebBrowser::ExecuteLobby() {
     LOG_WARNING(Service_AM, "(STUBBED) called, Lobby Applet is not implemented");
     WebBrowserExit(WebExitReason::EndButtonPressed);
 }
-
 } // namespace Service::AM::Applets

+ 10 - 0
src/core/hle/service/am/applets/web_browser.h

@@ -4,6 +4,8 @@
 
 #pragma once
 
+#include <optional>
+
 #include "common/common_funcs.h"
 #include "common/common_types.h"
 #include "core/hle/result.h"
@@ -32,6 +34,10 @@ public:
     void WebBrowserExit(WebExitReason exit_reason, std::string last_url = "");
 
 private:
+    bool InputTLVExistsInMap(WebArgInputTLVType input_tlv_type) const;
+
+    std::optional<std::vector<u8>> GetInputTLVData(WebArgInputTLVType input_tlv_type);
+
     // Initializers for the various types of browser applets
     void InitializeShop();
     void InitializeLogin();
@@ -56,9 +62,13 @@ private:
     ResultCode status{RESULT_SUCCESS};
 
     WebAppletVersion web_applet_version;
+    WebExitReason web_exit_reason;
     WebArgHeader web_arg_header;
     WebArgInputTLVMap web_arg_input_tlv_map;
 
+    std::string offline_cache_dir;
+    std::string offline_document;
+
     Core::System& system;
 };