[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>
This commit is contained in:
Maufeat
2026-01-17 01:48:15 +01:00
committed by crueter
parent b7417f68ce
commit ae501e256e
35 changed files with 2978 additions and 315 deletions
@@ -16,6 +16,10 @@
#include "core/hle/service/ns/read_only_application_control_data_interface.h"
#include "core/file_sys/patch_manager.h"
#include "frontend_common/firmware_manager.h"
#include "core/launch_timestamp_cache.h"
#include <algorithm>
#include <vector>
namespace Service::NS {
@@ -560,33 +564,42 @@ Result IApplicationManagerInterface::ListApplicationRecord(
s32 offset) {
const auto limit = out_records.size();
LOG_WARNING(Service_NS, "(STUBBED) called");
LOG_DEBUG(Service_NS, "called");
const auto& cache = system.GetContentProviderUnion();
const auto installed_games = cache.ListEntriesFilterOrigin(
std::nullopt, FileSys::TitleType::Application, FileSys::ContentRecordType::Program);
size_t i = 0;
u8 ii = 24;
std::vector<ApplicationRecord> records;
records.reserve(installed_games.size());
for (const auto& [slot, game] : installed_games) {
if (i >= limit) {
break;
}
if (game.title_id == 0 || game.title_id < 0x0100000000001FFFull) {
continue;
}
if (offset > 0) {
offset--;
continue;
}
if (game.title_id == 0 || game.title_id < 0x0100000000001FFFull) {
continue;
}
if ((game.title_id & 0xFFF) != 0) {
continue; // skip sub-programs (e.g., 001)
}
ApplicationRecord record{};
record.application_id = game.title_id;
record.type = ApplicationRecordType::Installed;
record.unknown = 0; // 2 = needs update
record.unknown2 = ii++;
ApplicationRecord record{};
record.application_id = game.title_id;
record.last_event = ApplicationEvent::Installed;
record.attributes = 0;
record.last_updated = Core::LaunchTimestampCache::GetLaunchTimestamp(game.title_id);
out_records[i++] = record;
records.push_back(record);
}
std::sort(records.begin(), records.end(), [](const ApplicationRecord& lhs, const ApplicationRecord& rhs) {
if (lhs.last_updated == rhs.last_updated) {
return lhs.application_id < rhs.application_id;
}
return lhs.last_updated > rhs.last_updated;
});
size_t i = 0;
const size_t start = static_cast<size_t>(std::max(0, offset));
for (size_t idx = start; idx < records.size() && i < limit; ++idx) {
out_records[i++] = records[idx];
}
*out_count = static_cast<s32>(i);
+4 -5
View File
@@ -12,7 +12,7 @@
namespace Service::NS {
enum class ApplicationRecordType : u8 {
enum class ApplicationEvent : u8 {
Installing = 2,
Installed = 3,
GameCardNotInserted = 5,
@@ -34,11 +34,10 @@ enum class BackgroundNetworkUpdateState : u8 {
struct ApplicationRecord {
u64 application_id;
ApplicationRecordType type;
u8 unknown;
ApplicationEvent last_event;
u8 attributes;
INSERT_PADDING_BYTES_NOINIT(0x6);
u8 unknown2;
INSERT_PADDING_BYTES_NOINIT(0x7);
s64 last_updated;
};
static_assert(sizeof(ApplicationRecord) == 0x18, "ApplicationRecord has incorrect size.");