|
|
@@ -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
|