Browse Source

qlaunch improvements :)

pull/3308/head
Maufeat 3 weeks ago
parent
commit
e8a164ee4c
  1. 2
      src/core/CMakeLists.txt
  2. 5
      src/core/core.cpp
  3. 3
      src/core/hle/service/am/service/application_creator.cpp
  4. 53
      src/core/hle/service/ns/application_manager_interface.cpp
  5. 9
      src/core/hle/service/ns/ns_types.h
  6. 135
      src/core/launch_timestamp_cache.cpp
  7. 13
      src/core/launch_timestamp_cache.h
  8. 5
      src/yuzu/game_list_worker.cpp

2
src/core/CMakeLists.txt

@ -1131,6 +1131,8 @@ add_library(core STATIC
internal_network/sockets.h
internal_network/wifi_scanner.cpp
internal_network/wifi_scanner.h
launch_timestamp_cache.cpp
launch_timestamp_cache.h
loader/deconstructed_rom_directory.cpp
loader/deconstructed_rom_directory.h
loader/kip.cpp

5
src/core/core.cpp

@ -15,6 +15,8 @@
#include "common/string_util.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "launch_timestamp_cache.h"
#include "core/core_timing.h"
#include "core/cpu_manager.h"
#include "core/debugger/debugger.h"
@ -331,6 +333,9 @@ struct System::Impl {
LOG_INFO(Core, "Loading {} ({:016X}) ...", name, params.program_id);
// Track launch time for frontend launches
LaunchTimestampCache::SaveLaunchTimestamp(params.program_id);
// Make the process created be the application
kernel.MakeApplicationProcess(process->GetHandle());

3
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();
}

53
src/core/hle/service/ns/application_manager_interface.cpp

@ -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.last_event = ApplicationEvent::Installed;
record.attributes = 0;
record.last_updated = Core::LaunchTimestampCache::GetLaunchTimestamp(game.title_id);
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;
});
ApplicationRecord record{};
record.application_id = game.title_id;
record.type = ApplicationRecordType::Installed;
record.unknown = 0; // 2 = needs update
record.unknown2 = ii++;
out_records[i++] = record;
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);

9
src/core/hle/service/ns/ns_types.h

@ -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.");

135
src/core/launch_timestamp_cache.cpp

@ -0,0 +1,135 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/launch_timestamp_cache.h"
#include <filesystem>
#include <fstream>
#include <mutex>
#include <sstream>
#include <string>
#include <unordered_map>
#include <fmt/format.h>
#include <nlohmann/json.hpp>
#include "common/fs/fs.h"
#include "common/fs/file.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
namespace Core::LaunchTimestampCache {
namespace {
using CacheMap = std::unordered_map<u64, s64>;
std::mutex mutex;
CacheMap cache;
bool loaded = false;
std::filesystem::path GetCachePath() {
return Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) / "launched.json";
}
std::optional<std::string> ReadFileToString(const std::filesystem::path& path) {
const std::ifstream file{path, std::ios::in | std::ios::binary};
if (!file) {
return std::nullopt;
}
std::ostringstream ss;
ss << file.rdbuf();
return ss.str();
}
bool WriteStringToFile(const std::filesystem::path& path, const std::string& data) {
if (!Common::FS::CreateParentDirs(path)) {
return false;
}
std::ofstream file{path, std::ios::out | std::ios::binary | std::ios::trunc};
if (!file) {
return false;
}
file.write(data.data(), static_cast<std::streamsize>(data.size()));
return static_cast<bool>(file);
}
void Load() {
if (loaded) {
return;
}
loaded = true;
const auto path = GetCachePath();
if (!std::filesystem::exists(path)) {
return;
}
const auto data = ReadFileToString(path);
if (!data) {
LOG_WARNING(Core, "Failed to read launch timestamp cache: {}",
Common::FS::PathToUTF8String(path));
return;
}
try {
const auto json = nlohmann::json::parse(data->data(), data->data() + data->size());
if (!json.is_object()) {
return;
}
for (auto it = json.begin(); it != json.end(); ++it) {
const auto key_str = it.key();
const auto value = it.value();
u64 key{};
try {
key = std::stoull(key_str, nullptr, 16);
} catch (...) {
continue;
}
if (value.is_number_integer()) {
cache[key] = value.get<s64>();
}
}
} catch (const std::exception& e) {
LOG_WARNING(Core, "Failed to parse launch timestamp cache");
}
}
void Save() {
nlohmann::json json = nlohmann::json::object();
for (const auto& [key, value] : cache) {
json[fmt::format("{:016X}", key)] = value;
}
const auto path = GetCachePath();
if (!WriteStringToFile(path, json.dump(4))) {
LOG_WARNING(Core, "Failed to write launch timestamp cache: {}",
Common::FS::PathToUTF8String(path));
}
}
s64 NowSeconds() {
return std::time(nullptr);
}
} // namespace
void SaveLaunchTimestamp(u64 title_id) {
std::scoped_lock lk{mutex};
Load();
cache[title_id] = NowSeconds();
Save();
}
s64 GetLaunchTimestamp(u64 title_id) {
std::scoped_lock lk{mutex};
Load();
const auto it = cache.find(title_id);
if (it != cache.end()) {
return it->second;
}
// we need a timestamp, i decided on 01/01/2026 00:00
return 1767225600;
}
} // namespace Core::LaunchTimestampCache

13
src/core/launch_timestamp_cache.h

@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_types.h"
namespace Core::LaunchTimestampCache {
void SaveLaunchTimestamp(u64 title_id);
s64 GetLaunchTimestamp(u64 title_id);
} // namespace Core::LaunchTimestampCache

5
src/yuzu/game_list_worker.cpp

@ -400,6 +400,11 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
if (res2 == Loader::ResultStatus::Success && program_ids.size() > 1 &&
(file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
for (const auto id : program_ids) {
// dravee suggested this, only viable way to
// not show sub-games in qlaunch for now.
if ((id & 0xFFF) != 0) {
continue;
}
loader = Loader::GetLoader(system, file, id);
if (!loader) {
continue;

Loading…
Cancel
Save