diff options
| author | Maufeat <sahyno1996@gmail.com> | 2026-01-17 01:48:15 +0100 |
|---|---|---|
| committer | crueter <crueter@eden-emu.dev> | 2026-01-17 01:48:15 +0100 |
| commit | ae501e256ea462268dbae8109ad07ef457c4fd8e (patch) | |
| tree | b36691663a10be26462d68f562ad51b8c697ee7b /src/core/hle/service/am | |
| parent | b7417f68ce10145e5eb282110e7d50f46ec8047b (diff) | |
[bcat/news/web/am] Implement news applet, proper TLV return, external web browser URL and qlaunch app sorting (#3308)
This pulls eden releases changelog & text from our github releases.
We don't store the msgpack file but rather generate them in-memory for the News Applet.
Uses cache folder. Files generated are:
- cache/news/github_releases.json
- cache/news/eden_logo.jpg
- cache/news/news_read
Additional changes:
- Proper TLV returning for online web applet, to open external URL
- Add applet type `LHub` to properly close, as it also uses TLV return
- qlaunch app sorting, adds another cached .json to track last launched app timestamps and sort them accordingly
Co-authored-by: crueter <crueter@eden-emu.dev>
Co-authored-by: DraVee <dravee@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3308
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: DraVee <dravee@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Co-authored-by: Maufeat <sahyno1996@gmail.com>
Co-committed-by: Maufeat <sahyno1996@gmail.com>
Diffstat (limited to 'src/core/hle/service/am')
6 files changed, 136 insertions, 11 deletions
diff --git a/src/core/hle/service/am/am_types.h b/src/core/hle/service/am/am_types.h index 621a7b4921..beb52b74dd 100644 --- a/src/core/hle/service/am/am_types.h +++ b/src/core/hle/service/am/am_types.h @@ -94,6 +94,7 @@ enum class AppletId : u32 { LoginShare = 0x18, WebAuth = 0x19, MyPage = 0x1A, + Lhub = 0x35 }; enum class AppletProgramId : u64 { diff --git a/src/core/hle/service/am/frontend/applet_web_browser.cpp b/src/core/hle/service/am/frontend/applet_web_browser.cpp index 8246b3e88e..e7cbdf6361 100644 --- a/src/core/hle/service/am/frontend/applet_web_browser.cpp +++ b/src/core/hle/service/am/frontend/applet_web_browser.cpp @@ -236,10 +236,6 @@ WebBrowser::WebBrowser(Core::System& system_, std::shared_ptr<Applet> applet_, WebBrowser::~WebBrowser() = default; void WebBrowser::Initialize() { - if (Settings::values.disable_web_applet) { - return; - } - FrontendApplet::Initialize(); LOG_INFO(Service_AM, "Initializing Web Browser Applet."); @@ -264,6 +260,12 @@ void WebBrowser::Initialize() { LOG_DEBUG(Service_AM, "WebArgHeader: total_tlv_entries={}, shim_kind={}", web_arg_header.total_tlv_entries, web_arg_header.shim_kind); + if (Settings::values.disable_web_applet && + web_arg_header.shim_kind != ShimKind::Web && + web_arg_header.shim_kind != ShimKind::Lhub) { + return; + } + ExtractSharedFonts(system); switch (web_arg_header.shim_kind) { @@ -288,6 +290,9 @@ void WebBrowser::Initialize() { case ShimKind::Lobby: InitializeLobby(); break; + case ShimKind::Lhub: + InitializeLhub(); + break; default: ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind); break; @@ -303,8 +308,19 @@ void WebBrowser::ExecuteInteractive() { } void WebBrowser::Execute() { + if (web_arg_header.shim_kind == ShimKind::Web) { + ExecuteWeb(); + return; + } + + if (web_arg_header.shim_kind == ShimKind::Lhub) { + ExecuteLhub(); + return; + } + if (Settings::values.disable_web_applet) { - LOG_WARNING(Service_AM, "(STUBBED) called, Web Browser Applet is disabled"); + LOG_WARNING(Service_AM, "(STUBBED) called, Web Browser Applet is disabled. shim_kind={}", + web_arg_header.shim_kind); WebBrowserExit(WebExitReason::EndButtonPressed); return; } @@ -331,6 +347,9 @@ void WebBrowser::Execute() { case ShimKind::Lobby: ExecuteLobby(); break; + case ShimKind::Lhub: + ExecuteLhub(); + break; default: ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind); WebBrowserExit(WebExitReason::EndButtonPressed); @@ -351,17 +370,99 @@ void WebBrowser::ExtractOfflineRomFS() { } void WebBrowser::WebBrowserExit(WebExitReason exit_reason, std::string last_url) { - if ((web_arg_header.shim_kind == ShimKind::Share && + const bool use_tlv_output = + (web_arg_header.shim_kind == ShimKind::Share && web_applet_version >= WebAppletVersion::Version196608) || (web_arg_header.shim_kind == ShimKind::Web && - web_applet_version >= WebAppletVersion::Version524288)) { - // TODO: Push Output TLVs instead of a WebCommonReturnValue + web_applet_version >= WebAppletVersion::Version524288) || + (web_arg_header.shim_kind == ShimKind::Lhub); + + // https://switchbrew.org/wiki/Internet_Browser#TLVs + if (use_tlv_output) { + LOG_DEBUG(Service_AM, "Using TLV output: exit_reason={}, last_url={}, last_url_size={}", + exit_reason, last_url, last_url.size()); + + // storage size for TLVs is 0x2000 bytes (as per switchbrew documentation) + constexpr size_t TLV_STORAGE_SIZE = 0x2000; + std::vector<u8> out_data(TLV_STORAGE_SIZE, 0); + + size_t current_offset = sizeof(WebArgHeader); + u16 tlv_count = 0; + + // align and matchng TLV struct alignment + auto align_offset = [](size_t offset) -> size_t { + return (offset + 7) & ~static_cast<size_t>(7); + }; + + // 0x1 ShareExitReason + { + WebArgOutputTLV tlv{}; + tlv.output_tlv_type = WebArgOutputTLVType::ShareExitReason; + tlv.arg_data_size = sizeof(u32); + + std::memcpy(out_data.data() + current_offset, &tlv, sizeof(WebArgOutputTLV)); + current_offset += sizeof(WebArgOutputTLV); + + const u32 exit_reason_value = static_cast<u32>(exit_reason); + std::memcpy(out_data.data() + current_offset, &exit_reason_value, sizeof(u32)); + current_offset += sizeof(u32); + + current_offset = align_offset(current_offset); + tlv_count++; + } + + // 0x2 LastUrl + { + WebArgOutputTLV tlv{}; + tlv.output_tlv_type = WebArgOutputTLVType::LastURL; + const u16 url_data_size = static_cast<u16>(last_url.size() + 1); + tlv.arg_data_size = url_data_size; + + std::memcpy(out_data.data() + current_offset, &tlv, sizeof(WebArgOutputTLV)); + current_offset += sizeof(WebArgOutputTLV); + + // null terminator + std::memcpy(out_data.data() + current_offset, last_url.c_str(), last_url.size() + 1); + current_offset += url_data_size; + current_offset = align_offset(current_offset); + tlv_count++; + } + + // 0x3 LastUrlSize + { + WebArgOutputTLV tlv{}; + tlv.output_tlv_type = WebArgOutputTLVType::LastURLSize; + tlv.arg_data_size = sizeof(u64); + + std::memcpy(out_data.data() + current_offset, &tlv, sizeof(WebArgOutputTLV)); + current_offset += sizeof(WebArgOutputTLV); + + const u64 url_size = last_url.size(); + std::memcpy(out_data.data() + current_offset, &url_size, sizeof(u64)); + current_offset += sizeof(u64); + tlv_count++; + } + + WebArgHeader out_header{}; + out_header.total_tlv_entries = tlv_count; + out_header.shim_kind = web_arg_header.shim_kind; + std::memcpy(out_data.data(), &out_header, sizeof(WebArgHeader)); + + LOG_DEBUG(Service_AM, "TLV output: total_size={}, tlv_count={}, used_offset={}", + out_data.size(), tlv_count, current_offset); + + complete = true; + PushOutData(std::make_shared<IStorage>(system, std::move(out_data))); + Exit(); + return; } - WebCommonReturnValue web_common_return_value; + // for old browser, keep old return, use WebCommonReturnValue + WebCommonReturnValue web_common_return_value{}; web_common_return_value.exit_reason = exit_reason; - std::memcpy(&web_common_return_value.last_url, last_url.data(), last_url.size()); + std::memcpy(&web_common_return_value.last_url, last_url.data(), + std::min(last_url.size(), web_common_return_value.last_url.size())); web_common_return_value.last_url_size = last_url.size(); LOG_DEBUG(Service_AM, "WebCommonReturnValue: exit_reason={}, last_url={}, last_url_size={}", @@ -516,4 +617,13 @@ void WebBrowser::ExecuteLobby() { LOG_WARNING(Service_AM, "(STUBBED) called, Lobby Applet is not implemented"); WebBrowserExit(WebExitReason::EndButtonPressed); } + +void WebBrowser::InitializeLhub() {} + +void WebBrowser::ExecuteLhub() { + LOG_INFO(Service_AM, "(STUBBED) called, Lhub Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} + + } // namespace Service::AM::Frontend diff --git a/src/core/hle/service/am/frontend/applet_web_browser.h b/src/core/hle/service/am/frontend/applet_web_browser.h index ba20b7a4cf..ae62389f13 100644 --- a/src/core/hle/service/am/frontend/applet_web_browser.h +++ b/src/core/hle/service/am/frontend/applet_web_browser.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -53,6 +56,7 @@ private: void InitializeWeb(); void InitializeWifi(); void InitializeLobby(); + void InitializeLhub(); // Executors for the various types of browser applets void ExecuteShop(); @@ -62,6 +66,7 @@ private: void ExecuteWeb(); void ExecuteWifi(); void ExecuteLobby(); + void ExecuteLhub(); const Core::Frontend::WebBrowserApplet& frontend; diff --git a/src/core/hle/service/am/frontend/applet_web_browser_types.h b/src/core/hle/service/am/frontend/applet_web_browser_types.h index 2f7c05c243..0892a6a716 100644 --- a/src/core/hle/service/am/frontend/applet_web_browser_types.h +++ b/src/core/hle/service/am/frontend/applet_web_browser_types.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -30,6 +33,7 @@ enum class ShimKind : u32 { Web = 5, Wifi = 6, Lobby = 7, + Lhub = 8, }; enum class WebExitReason : u32 { diff --git a/src/core/hle/service/am/frontend/applets.cpp b/src/core/hle/service/am/frontend/applets.cpp index a25b7e3aa2..2ff2bcdbb6 100644 --- a/src/core/hle/service/am/frontend/applets.cpp +++ b/src/core/hle/service/am/frontend/applets.cpp @@ -237,13 +237,15 @@ std::shared_ptr<FrontendApplet> FrontendAppletHolder::GetApplet(std::shared_ptr< case AppletId::OfflineWeb: case AppletId::LoginShare: case AppletId::WebAuth: + case AppletId::Lhub: return std::make_shared<WebBrowser>(system, applet, mode, *frontend.web_browser); case AppletId::PhotoViewer: return std::make_shared<PhotoViewer>(system, applet, mode, *frontend.photo_viewer); case AppletId::NetConnect: return std::make_shared<NetConnect>(system, applet, mode, *frontend.net_connect); default: - LOG_ERROR(Service_AM, "No backend implementation exists for applet_id={:02X}. Falling back to stub applet", static_cast<u8>(id)); + LOG_ERROR(Service_AM, "No backend implementation exists for applet_id={:02X} program_id={:016X}" + "Falling back to stub applet", static_cast<u8>(id), applet->program_id); return std::make_shared<StubApplet>(system, applet, id, mode); } } diff --git a/src/core/hle/service/am/service/application_creator.cpp b/src/core/hle/service/am/service/application_creator.cpp index 2fc33a303c..d16fd7dd84 100644 --- a/src/core/hle/service/am/service/application_creator.cpp +++ b/src/core/hle/service/am/service/application_creator.cpp @@ -15,6 +15,7 @@ #include "core/hle/service/am/window_system.h" #include "core/hle/service/cmif_serialization.h" #include "core/loader/loader.h" +#include "core/launch_timestamp_cache.h" namespace Service::AM { @@ -72,6 +73,7 @@ IApplicationCreator::~IApplicationCreator() = default; Result IApplicationCreator::CreateApplication( Out<SharedPointer<IApplicationAccessor>> out_application_accessor, u64 application_id) { LOG_INFO(Service_NS, "called, application_id={:016X}", application_id); + Core::LaunchTimestampCache::SaveLaunchTimestamp(application_id); R_RETURN( CreateGuestApplication(out_application_accessor, system, m_window_system, application_id)); } @@ -103,6 +105,7 @@ Result IApplicationCreator::CreateSystemApplication( *out_application_accessor = std::make_shared<IApplicationAccessor>(system, applet, m_window_system); + Core::LaunchTimestampCache::SaveLaunchTimestamp(application_id); R_SUCCEED(); } |
