Browse Source

changes for sorting game titles

pull/3327/head
Maufeat 3 weeks ago
parent
commit
ae8e08637f
  1. 8
      src/core/file_sys/control_metadata.cpp
  2. 1
      src/core/file_sys/control_metadata.h
  3. 10
      src/core/hle/service/erpt/erpt.cpp
  4. 15
      src/core/hle/service/friend/friend.cpp
  5. 9
      src/core/hle/service/ns/content_management_interface.cpp
  6. 1
      src/core/hle/service/ns/content_management_interface.h
  7. 33
      src/core/hle/service/ns/query_service.cpp
  8. 16
      src/core/hle/service/ns/query_service.h
  9. 64
      src/core/hle/service/ns/read_only_application_control_data_interface.cpp
  10. 1
      src/core/hle/service/ns/read_only_application_control_data_interface.h
  11. 12
      src/core/hle/service/sockets/nsd.cpp
  12. 1
      src/core/hle/service/sockets/nsd.h
  13. 55
      src/core/launch_timestamp_cache.cpp
  14. 1
      src/core/launch_timestamp_cache.h

8
src/core/file_sys/control_metadata.cpp

@ -85,6 +85,14 @@ const LanguageEntry& NACP::GetLanguageEntry() const {
return raw.language_entries.at(static_cast<u8>(Language::AmericanEnglish));
}
std::array<std::string, 16> NACP::GetApplicationNames() const {
std::array<std::string, 16> names{};
for (size_t i = 0; i < raw.language_entries.size(); ++i) {
names[i] = raw.language_entries[i].GetApplicationName();
}
return names;
}
std::string NACP::GetApplicationName() const {
return GetLanguageEntry().GetApplicationName();
}

1
src/core/file_sys/control_metadata.h

@ -131,6 +131,7 @@ public:
u64 GetDefaultNormalSaveSize() const;
u64 GetDefaultJournalSaveSize() const;
u32 GetSupportedLanguages() const;
std::array<std::string, 16> GetApplicationNames() const;
std::vector<u8> GetRawBytes() const;
bool GetUserAccountSwitchLock() const;
u64 GetDeviceSaveDataSize() const;

10
src/core/hle/service/erpt/erpt.cpp

@ -22,7 +22,7 @@ public:
{2, nullptr, "SetInitialLaunchSettingsCompletionTime"},
{3, nullptr, "ClearInitialLaunchSettingsCompletionTime"},
{4, nullptr, "UpdatePowerOnTime"},
{5, nullptr, "UpdateAwakeTime"},
{5, D<&ErrorReportContext::UpdateAwakeTime>, "UpdateAwakeTime"},
{6, nullptr, "SubmitMultipleCategoryContext"},
{7, nullptr, "UpdateApplicationLaunchTime"},
{8, nullptr, "ClearApplicationLaunchTime"},
@ -74,6 +74,14 @@ private:
report_type, unknown, create_report_option_flag);
R_SUCCEED();
}
Result UpdateAwakeTime(InBuffer<BufferAttr_HipcMapAlias> data_a,
InBuffer<BufferAttr_HipcMapAlias> data_b, u32 flag_a, u32 flag_b) {
LOG_WARNING(Service_SET,
"(STUBBED) called, data_a_size={}, data_b_size={}, flag_a={}, flag_b={}",
data_a.size(), data_b.size(), flag_a, flag_b);
R_SUCCEED();
}
};
class ErrorReportSession final : public ServiceFramework<ErrorReportSession> {

15
src/core/hle/service/friend/friend.cpp

@ -53,6 +53,7 @@ public:
{20102, nullptr, "GetFriendDetailedInfo"},
{20103, nullptr, "SyncFriendList"},
{20104, &IFriendService::RequestSyncFriendList, "RequestSyncFriendList"},
{20105, &IFriendService::GetFriendListForViewer, "GetFriendListForViewer"},
{20110, nullptr, "LoadFriendSetting"},
{20200, &IFriendService::GetReceivedFriendRequestCount, "GetReceivedFriendRequestCount"},
{20201, nullptr, "GetFriendRequestList"},
@ -65,11 +66,13 @@ public:
{20401, nullptr, "SyncBlockedUserList"},
{20500, nullptr, "GetProfileExtraList"},
{20501, nullptr, "GetRelationship"},
{20600, &IFriendService::GetUserPresenceView, "GetUserPresenceView"},
{20600, &IFriendService::GetUserPresenceView, "GetUserPresenceViewV1"},
{20601, &IFriendService::GetUserPresenceView, "GetUserPresenceViewV2"},
{20700, nullptr, "GetPlayHistoryList"},
{20701, &IFriendService::GetPlayHistoryStatistics, "GetPlayHistoryStatistics"},
{20800, &IFriendService::LoadUserSetting, "LoadUserSetting"},
{20800, &IFriendService::LoadUserSetting, "LoadUserSettingV1"},
{20801, nullptr, "SyncUserSetting"},
{20802, &IFriendService::LoadUserSetting, "LoadUserSettingV2"},
{20900, &IFriendService::RequestListSummaryOverlayNotification, "RequestListSummaryOverlayNotification"},
{21000, nullptr, "GetExternalApplicationCatalog"},
{22000, nullptr, "GetReceivedFriendInvitationList"},
@ -270,6 +273,14 @@ private:
rb.Push(ResultSuccess);
}
void GetFriendListForViewer(HLERequestContext& ctx) {
LOG_DEBUG(Service_Friend, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(0);
}
void GetReceivedFriendRequestCount(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
[[maybe_unused]] const auto uuid = rp.PopRaw<Common::UUID>();

9
src/core/hle/service/ns/content_management_interface.cpp

@ -21,6 +21,7 @@ IContentManagementInterface::IContentManagementInterface(Core::System& system_)
{43, D<&IContentManagementInterface::CheckSdCardMountStatus>, "CheckSdCardMountStatus"},
{47, D<&IContentManagementInterface::GetTotalSpaceSize>, "GetTotalSpaceSize"},
{48, D<&IContentManagementInterface::GetFreeSpaceSize>, "GetFreeSpaceSize"},
{71, D<&IContentManagementInterface::GetUnknown71>, "Unknown71"},
{600, nullptr, "CountApplicationContentMeta"},
{601, nullptr, "ListApplicationContentMetaStatus"},
{605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
@ -73,4 +74,12 @@ Result IContentManagementInterface::GetFreeSpaceSize(Out<s64> out_free_space_siz
R_SUCCEED();
}
Result IContentManagementInterface::GetUnknown71(Out<u64> out_value_a, Out<u64> out_value_b,
u8 flag) {
LOG_INFO(Service_NS, "(STUBBED) called, flag={:02X}", flag);
*out_value_a = 0;
*out_value_b = 0;
R_SUCCEED();
}
} // namespace Service::NS

1
src/core/hle/service/ns/content_management_interface.h

@ -20,6 +20,7 @@ public:
Result CheckSdCardMountStatus();
Result GetTotalSpaceSize(Out<s64> out_total_space_size, FileSys::StorageId storage_id);
Result GetFreeSpaceSize(Out<s64> out_free_space_size, FileSys::StorageId storage_id);
Result GetUnknown71(Out<u64> out_value_a, Out<u64> out_value_b, u8 flag);
};
} // namespace Service::NS

33
src/core/hle/service/ns/query_service.cpp

@ -9,6 +9,7 @@
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ns/query_service.h"
#include "core/hle/service/service.h"
#include "core/launch_timestamp_cache.h"
namespace Service::NS {
@ -33,8 +34,8 @@ IQueryService::IQueryService(Core::System& system_) : ServiceFramework{system_,
{15, nullptr, "GetRecentlyPlayedApplicationUpdateEvent"},
{16, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystemV0"},
{17, D<&IQueryService::QueryLastPlayTime>, "QueryLastPlayTime"},
{18, nullptr, "QueryApplicationPlayStatisticsForSystem"},
{19, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystem"},
{18, D<&IQueryService::QueryApplicationPlayStatisticsForSystem>, "QueryApplicationPlayStatisticsForSystem"},
{19, D<&IQueryService::QueryApplicationPlayStatisticsByUserAccountIdForSystem>, "QueryApplicationPlayStatisticsByUserAccountIdForSystem"},
};
// clang-format on
@ -65,4 +66,32 @@ Result IQueryService::QueryLastPlayTime(
R_SUCCEED();
}
Result IQueryService::QueryApplicationPlayStatisticsForSystem(
Out<s32> out_entries, u8 flag,
OutArray<ApplicationPlayStatistics, BufferAttr_HipcMapAlias> out_stats,
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
const size_t count = std::min(out_stats.size(), application_ids.size());
s32 written = 0;
for (size_t i = 0; i < count; ++i) {
const u64 app_id = application_ids[i];
ApplicationPlayStatistics stats{};
stats.application_id = app_id;
stats.play_time_ns = Core::LaunchTimestampCache::GetLaunchTimestamp(app_id) * 1'000'000'000ULL;
stats.launch_count = Core::LaunchTimestampCache::GetLaunchCount(app_id);
out_stats[i] = stats;
++written;
}
*out_entries = written;
LOG_DEBUG(Service_NS, "called, entries={} flag={}", written, flag);
R_SUCCEED();
}
Result IQueryService::QueryApplicationPlayStatisticsByUserAccountIdForSystem(
Out<s32> out_entries, u8 flag, Common::UUID user_id,
OutArray<ApplicationPlayStatistics, BufferAttr_HipcMapAlias> out_stats,
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
// well we don't do per-user tracking :>
return QueryApplicationPlayStatisticsForSystem(out_entries, flag, out_stats, application_ids);
}
} // namespace Service::NS

16
src/core/hle/service/ns/query_service.h

@ -7,6 +7,7 @@
#pragma once
#include "common/uuid.h"
#include "core/hle/service/am/am_types.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/ns/ns_types.h"
#include "core/hle/service/service.h"
@ -28,6 +29,13 @@ static_assert(sizeof(PlayStatistics) == 0x28, "PlayStatistics is an invalid size
struct LastPlayTime {};
struct ApplicationPlayStatistics {
u64 application_id{};
u64 play_time_ns{};
u64 launch_count{};
};
static_assert(sizeof(ApplicationPlayStatistics) == 0x18, "ApplicationPlayStatistics is an invalid size");
class IQueryService final : public ServiceFramework<IQueryService> {
public:
explicit IQueryService(Core::System& system_);
@ -39,6 +47,14 @@ private:
Result QueryLastPlayTime(Out<s32> out_entries, u8 unknown,
OutArray<LastPlayTime, BufferAttr_HipcMapAlias> out_last_play_times,
InArray<s32, BufferAttr_HipcMapAlias> application_ids);
Result QueryApplicationPlayStatisticsForSystem(
Out<s32> out_entries, u8 flag,
OutArray<ApplicationPlayStatistics, BufferAttr_HipcMapAlias> out_stats,
InArray<u64, BufferAttr_HipcMapAlias> application_ids);
Result QueryApplicationPlayStatisticsByUserAccountIdForSystem(
Out<s32> out_entries, u8 flag, Common::UUID user_id,
OutArray<ApplicationPlayStatistics, BufferAttr_HipcMapAlias> out_stats,
InArray<u64, BufferAttr_HipcMapAlias> application_ids);
};
} // namespace Service::NS

64
src/core/hle/service/ns/read_only_application_control_data_interface.cpp

@ -6,9 +6,11 @@
#include <algorithm>
#include <vector>
#include <optional>
#include <stb_image.h>
#include <stb_image_resize.h>
#include <stb_image_write.h>
#include <string>
#include "common/settings.h"
#include "core/file_sys/control_metadata.h"
@ -20,6 +22,7 @@
#include "core/hle/service/ns/ns_results.h"
#include "core/hle/service/ns/read_only_application_control_data_interface.h"
#include "core/hle/service/set/settings_server.h"
#include "core/hle/service/kernel_helpers.h"
namespace Service::NS {
@ -66,6 +69,37 @@ void SanitizeJPEGImageSize(std::vector<u8>& image) {
} // namespace
class AsyncValue final : public ServiceFramework<AsyncValue> {
public:
explicit AsyncValue(Core::System& system_)
: ServiceFramework{system_, "IAsyncValue"}, service_context{system_, "IAsyncValue"} {
static const FunctionInfo functions[] = {
{0, &AsyncValue::Get, "Get"},
};
RegisterHandlers(functions);
completion_event = service_context.CreateEvent("IAsyncValue:Completion");
completion_event->GetReadableEvent().Signal();
}
~AsyncValue() override {
service_context.CloseEvent(completion_event);
}
Kernel::KReadableEvent& ReadableEvent() const {
return completion_event->GetReadableEvent();
}
private:
void Get(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
KernelHelpers::ServiceContext service_context;
Kernel::KEvent* completion_event{};
};
IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterface(
Core::System& system_)
: ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
@ -76,8 +110,9 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
{2, D<&IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLanguageCode>, "ConvertApplicationLanguageToLanguageCode"},
{3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
{4, nullptr, "SelectApplicationDesiredLanguage"},
{5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData2>, "GetApplicationControlDataWithoutIcon"},
{19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData3>, "GetApplicationControlDataWithoutIcon3"},
{5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData2>, "GetApplicationControlData"},
{13, &IReadOnlyApplicationControlDataInterface::GetApplicationTitle, "GetApplicationTitle"},
{19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData3>, "GetApplicationControlData"},
};
// clang-format on
@ -240,6 +275,31 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData2(
R_SUCCEED();
}
// Testing for Unknown command, reverify with real switch. This is for testing!
void IReadOnlyApplicationControlDataInterface::GetApplicationTitle(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 language_mask = rp.Pop<u64>();
u64 application_id = 0;
const auto app_buf = ctx.ReadBuffer();
if (app_buf.size() >= sizeof(u64)) {
std::memcpy(&application_id, app_buf.data(), sizeof(u64));
}
const auto input_handle = ctx.GetCopyHandle(0);
auto async_value = std::make_shared<AsyncValue>(system);
LOG_INFO(Service_NS,
"called with application_id={:016X}, language_mask={:016X}, input_handle=0x{:X}",
application_id, language_mask, input_handle);
IPC::ResponseBuilder rb{ctx, 2, 1, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(async_value->ReadableEvent());
rb.PushIpcInterface(std::move(async_value));
}
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3(
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_flags_a, Out<u32> out_flags_b,
Out<u32> out_actual_size, ApplicationControlSource application_control_source, u8 flag1, u8 flag2, u64 application_id) {

1
src/core/hle/service/ns/read_only_application_control_data_interface.h

@ -34,6 +34,7 @@ public:
u8 flag1,
u8 flag2,
u64 application_id);
void GetApplicationTitle(HLERequestContext& ctx);
Result GetApplicationControlData3(
OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
Out<u32> out_flags_a,

12
src/core/hle/service/sockets/nsd.cpp

@ -34,7 +34,7 @@ NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, na
{12, nullptr, "GetDeviceId"},
{13, nullptr, "DeleteSettings"},
{14, nullptr, "ImportSettings"},
{15, nullptr, "SetChangeEnvironmentIdentifierDisabled"},
{15, &NSD::SetChangeEnvironmentIdentifierDisabled, "SetChangeEnvironmentIdentifierDisabled"},
{20, &NSD::Resolve, "Resolve"},
{21, &NSD::ResolveEx, "ResolveEx"},
{30, nullptr, "GetNasServiceSetting"},
@ -77,6 +77,16 @@ static Result ResolveCommon(const std::string& fqdn_in, std::array<char, 0x100>&
return ResultSuccess;
}
void NSD::SetChangeEnvironmentIdentifierDisabled(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const bool disabled = rp.Pop<bool>();
LOG_WARNING(Service, "(STUBBED) called, disabled={}", disabled);
IPC::ResponseBuilder rb{ctx, 1};
rb.Push(ResultSuccess);
}
void NSD::Resolve(HLERequestContext& ctx) {
const std::string fqdn_in = Common::StringFromBuffer(ctx.ReadBuffer(0));

1
src/core/hle/service/sockets/nsd.h

@ -17,6 +17,7 @@ public:
~NSD() override;
private:
void SetChangeEnvironmentIdentifierDisabled(HLERequestContext& ctx);
void Resolve(HLERequestContext& ctx);
void ResolveEx(HLERequestContext& ctx);
void GetEnvironmentIdentifier(HLERequestContext& ctx);

55
src/core/launch_timestamp_cache.cpp

@ -22,10 +22,12 @@ namespace Core::LaunchTimestampCache {
namespace {
using CacheMap = std::unordered_map<u64, s64>;
using CountMap = std::unordered_map<u64, u64>;
std::mutex mutex;
CacheMap cache;
bool loaded = false;
std::mutex g_mutex;
CacheMap g_cache;
CountMap g_counts;
bool g_loaded = false;
std::filesystem::path GetCachePath() {
return Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) / "launched.json";
@ -54,11 +56,11 @@ bool WriteStringToFile(const std::filesystem::path& path, const std::string& dat
}
void Load() {
if (loaded) {
if (g_loaded) {
return;
}
loaded = true;
g_loaded = true;
const auto path = GetCachePath();
if (!std::filesystem::exists(path)) {
@ -86,19 +88,31 @@ void Load() {
} catch (...) {
continue;
}
if (value.is_number_integer()) {
cache[key] = value.get<s64>();
if (value.is_object()) {
if (value.contains("timestamp") && value["timestamp"].is_number_integer()) {
g_cache[key] = value["timestamp"].get<s64>();
}
if (value.contains("launch_count") && value["launch_count"].is_number_unsigned()) {
g_counts[key] = value["launch_count"].get<u64>();
}
} else if (value.is_number_integer()) {
// Legacy format: raw timestamp only
g_cache[key] = value.get<s64>();
}
}
} catch (const std::exception& e) {
LOG_WARNING(Core, "Failed to parse launch timestamp cache");
LOG_WARNING(Core, "Failed to parse launch timestamp cache: {}", e.what());
}
}
void Save() {
nlohmann::json json = nlohmann::json::object();
for (const auto& [key, value] : cache) {
json[fmt::format("{:016X}", key)] = value;
for (const auto& [key, value] : g_cache) {
nlohmann::json entry = nlohmann::json::object();
entry["timestamp"] = value;
const auto count_it = g_counts.find(key);
entry["launch_count"] = count_it != g_counts.end() ? count_it->second : 0;
json[fmt::format("{:016X}", key)] = entry;
}
const auto path = GetCachePath();
@ -115,21 +129,32 @@ s64 NowSeconds() {
} // namespace
void SaveLaunchTimestamp(u64 title_id) {
std::scoped_lock lk{mutex};
std::scoped_lock lk{g_mutex};
Load();
cache[title_id] = NowSeconds();
g_cache[title_id] = NowSeconds();
g_counts[title_id] = g_counts[title_id] + 1;
Save();
}
s64 GetLaunchTimestamp(u64 title_id) {
std::scoped_lock lk{mutex};
std::scoped_lock lk{g_mutex};
Load();
const auto it = cache.find(title_id);
if (it != cache.end()) {
const auto it = g_cache.find(title_id);
if (it != g_cache.end()) {
return it->second;
}
// we need a timestamp, i decided on 01/01/2026 00:00
return 1767225600;
}
u64 GetLaunchCount(u64 title_id) {
std::scoped_lock lk{g_mutex};
Load();
const auto it = g_counts.find(title_id);
if (it != g_counts.end()) {
return it->second;
}
return 0;
}
} // namespace Core::LaunchTimestampCache

1
src/core/launch_timestamp_cache.h

@ -9,5 +9,6 @@ namespace Core::LaunchTimestampCache {
void SaveLaunchTimestamp(u64 title_id);
s64 GetLaunchTimestamp(u64 title_id);
u64 GetLaunchCount(u64 title_id);
} // namespace Core::LaunchTimestampCache
Loading…
Cancel
Save