From 89df4835673a2c6b1905459b2aff866a6caeec4a Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Thu, 12 Nov 2020 12:23:46 -0500 Subject: [PATCH] applets/web: Implement the offline browser applet backend --- .../hle/service/am/applets/web_browser.cpp | 146 ++++++++++++++++-- src/core/hle/service/am/applets/web_browser.h | 10 ++ 2 files changed, 143 insertions(+), 13 deletions(-) diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp index 7f398254e..06fcf3e3f 100644 --- a/src/core/hle/service/am/applets/web_browser.cpp +++ b/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 - #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& 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& 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& web_arg, WebArgHeader& web_ return input_tlv_map; } -std::optional> 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 (map_it == input_tlv_map.end()) { - return std::nullopt; + 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); + } + + return nca->GetRomFS(); + } else { + const auto nca = system.GetContentProvider().GetEntry(title_id, nca_type); + + 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); } - - return map_it->second; } } // 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> 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(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(GetInputTLVData(WebArgInputTLVType::ApplicationID).value()); + nca_type = FileSys::ContentRecordType::LegalInformation; + break; + case DocumentKind::SystemDataPage: + title_id = ParseRawValue(GetInputTLVData(WebArgInputTLVType::SystemDataID).value()); + nca_type = FileSys::ContentRecordType::Data; + break; + } + + static constexpr std::array 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(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 diff --git a/src/core/hle/service/am/applets/web_browser.h b/src/core/hle/service/am/applets/web_browser.h index a1ed5fd1d..c36c717f1 100644 --- a/src/core/hle/service/am/applets/web_browser.h +++ b/src/core/hle/service/am/applets/web_browser.h @@ -4,6 +4,8 @@ #pragma once +#include + #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> 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; };