From 1847db06f8354b28f7d7339ed274902bd595a099 Mon Sep 17 00:00:00 2001 From: Alula Date: Wed, 18 Mar 2026 23:33:55 +0100 Subject: [PATCH] erpt: Implement 22.0.0 commands and changes Co-authored-by: nvnprogram <97150065+nvnprogram@users.noreply.github.com> --- .../include/stratosphere/erpt/erpt_types.hpp | 29 ++++ .../erpt/sf/erpt_sf_i_manager.hpp | 1 + .../stratosphere/erpt/srv/erpt_srv_api.hpp | 4 +- .../srv/erpt_srv_journal_for_attachments.cpp | 2 +- .../source/erpt/srv/erpt_srv_main.cpp | 92 ++++++++++++- .../source/erpt/srv/erpt_srv_manager_impl.cpp | 10 +- .../source/erpt/srv/erpt_srv_manager_impl.hpp | 1 + .../erpt/srv/erpt_srv_recent_report.cpp | 93 +++++++++++++ .../erpt/srv/erpt_srv_recent_report.hpp | 28 ++++ .../source/erpt/srv/erpt_srv_reporter.cpp | 129 ++++++++++++++++-- .../source/erpt/srv/erpt_srv_reporter.hpp | 11 +- .../include/vapours/results/erpt_results.hpp | 1 + stratosphere/erpt/source/erpt_main.cpp | 57 +------- 13 files changed, 381 insertions(+), 77 deletions(-) create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_recent_report.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_recent_report.hpp diff --git a/libraries/libstratosphere/include/stratosphere/erpt/erpt_types.hpp b/libraries/libstratosphere/include/stratosphere/erpt/erpt_types.hpp index f210ab16f..9ae14fcef 100644 --- a/libraries/libstratosphere/include/stratosphere/erpt/erpt_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/erpt/erpt_types.hpp @@ -243,4 +243,33 @@ namespace ams::erpt { Map16 = 0xDE, }; + constexpr inline u32 ErrorCodeSizeMax = 15; + constexpr inline u32 ProgramIdSizeMax = 17; + + struct RecentReportEntry { + char error_code[ErrorCodeSizeMax]; + char program_id[ProgramIdSizeMax]; + u8 is_visible; + u8 is_system_abort; + u8 is_application_abort; + }; + static_assert(sizeof(RecentReportEntry) == 35); + + struct RecentReportSummary : public sf::LargeData, public sf::PrefersAutoSelectTransferMode { + u32 entry_count; + RecentReportEntry entries[50]; + char firmware_display_version[0x18]; + char private_os_version[96]; + char product_model[16]; + char region_code[34]; + }; + static_assert(sizeof(RecentReportSummary) == 0x784); + + struct SystemInfo { + char os_version[0x18]; + char private_os_version[96]; + char product_model[16]; + const char *region; + }; + } diff --git a/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_manager.hpp b/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_manager.hpp index e75bfb341..b91ade849 100644 --- a/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_manager.hpp +++ b/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_manager.hpp @@ -25,6 +25,7 @@ AMS_SF_METHOD_INFO(C, H, 4, Result, GetStorageUsageStatistics, (ams::sf::Out out), (out), hos::Version_5_0_0) \ AMS_SF_METHOD_INFO(C, H, 5, Result, GetAttachmentListDeprecated, (const ams::sf::OutBuffer &out_buf, const erpt::ReportId &report_id), (out_buf, report_id), hos::Version_8_0_0, hos::Version_19_0_1) \ AMS_SF_METHOD_INFO(C, H, 6, Result, GetAttachmentList, (ams::sf::Out out_count, const ams::sf::OutBuffer &out_buf, const erpt::ReportId &report_id), (out_count, out_buf, report_id), hos::Version_20_0_0) \ + AMS_SF_METHOD_INFO(C, H, 7, Result, GetRecentReportSummary, (ams::sf::Out out), (out), hos::Version_22_0_0) \ AMS_SF_METHOD_INFO(C, H, 10, Result, GetReportSizeMax, (ams::sf::Out out), (out), hos::Version_20_0_0) diff --git a/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_api.hpp b/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_api.hpp index 1feb10eeb..3d3ead665 100644 --- a/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_api.hpp @@ -21,7 +21,9 @@ namespace ams::erpt::srv { Result Initialize(u8 *mem, size_t mem_size); Result InitializeAndStartService(); - Result SetSerialNumberAndOsVersion(const char *sn, u32 sn_len, const char *os, u32 os_len, const char *os_priv, u32 os_priv_len); + const SystemInfo &GetSystemInfo(); + + Result SetSerialNumber(const char *sn, u32 sn_len); Result SetProductModel(const char *model, u32 model_len); Result SetRegionSetting(const char *region, u32 region_len); diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal_for_attachments.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal_for_attachments.cpp index 77c958fbf..d2a9bedfb 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal_for_attachments.cpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal_for_attachments.cpp @@ -84,7 +84,7 @@ namespace ams::erpt::srv { Result JournalForAttachments::GetAttachmentList(u32 *out_count, AttachmentInfo *out_infos, size_t max_out_infos, ReportId report_id) { if (hos::GetVersion() >= hos::Version_20_0_0) { /* TODO: What define gives a minimum of 10? */ - R_UNLESS(max_out_infos >= 10, erpt::ResultInvalidArgument()); + R_UNLESS(max_out_infos >= 10, erpt::ResultTooManyOutAttachments()); } u32 count = 0; diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_main.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_main.cpp index 2f853900d..f6103820b 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_main.cpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_main.cpp @@ -21,6 +21,7 @@ #include "erpt_srv_journal.hpp" #include "erpt_srv_service.hpp" #include "erpt_srv_forced_shutdown.hpp" +#include "erpt_srv_recent_report.hpp" namespace ams::erpt::srv { @@ -33,6 +34,7 @@ namespace ams::erpt::srv { constexpr u32 SystemSaveDataFlags = fs::SaveDataFlags_KeepAfterResettingSystemSaveDataWithoutUserSaveData; constexpr s64 SystemSaveDataSize = 11_MB; constexpr s64 SystemSaveDataJournalSize = 2720_KB; + constexpr u32 DefaultThrottleTimeWindowSeconds = 3; constinit bool g_automatic_report_cleanup_enabled = true; @@ -53,7 +55,7 @@ namespace ams::erpt::srv { } Result MountSystemSaveData() { - if (hos::GetVersion() < hos::Version_22_0_0) { + if (hos::GetVersion() < hos::Version_21_0_0) { fs::DisableAutoSaveDataCreation(); } @@ -73,6 +75,72 @@ namespace ams::erpt::srv { } + namespace { + + int MakeProductModelString(char *dst, size_t dst_size, settings::system::ProductModel model) { + switch (model) { + case settings::system::ProductModel_Invalid: return util::Strlcpy(dst, "Invalid", static_cast(dst_size)); + case settings::system::ProductModel_Nx: return util::Strlcpy(dst, "NX", static_cast(dst_size)); + default: return util::SNPrintf(dst, dst_size, "%d", static_cast(model)); + } + } + + const char *GetRegionString(settings::system::RegionCode code) { + switch (code) { + case settings::system::RegionCode_Japan: return "Japan"; + case settings::system::RegionCode_Usa: return "Usa"; + case settings::system::RegionCode_Europe: return "Europe"; + case settings::system::RegionCode_Australia: return "Australia"; + case settings::system::RegionCode_HongKongTaiwanKorea: return "HongKongTaiwanKorea"; + case settings::system::RegionCode_China: return "China"; + default: return "RegionUnknown"; + } + } + + } + + const erpt::SystemInfo &GetSystemInfo() { + static const erpt::SystemInfo s_info = [] { + erpt::SystemInfo info = {}; + + settings::system::FirmwareVersion firmware_version = {}; + settings::system::GetFirmwareVersion(std::addressof(firmware_version)); + + util::Strlcpy(info.os_version, firmware_version.display_version, sizeof(info.os_version)); + + const auto os_priv_len = util::SNPrintf(info.private_os_version, sizeof(info.private_os_version), "%s (%.8s)", firmware_version.display_name, firmware_version.revision); + AMS_ASSERT(static_cast(os_priv_len) < sizeof(info.private_os_version)); + AMS_UNUSED(os_priv_len); + + const auto pm_len = MakeProductModelString(info.product_model, sizeof(info.product_model), settings::system::GetProductModel()); + AMS_ASSERT(static_cast(pm_len) < sizeof(info.product_model)); + AMS_UNUSED(pm_len); + + settings::system::RegionCode region_code; + settings::system::GetRegionCode(std::addressof(region_code)); + info.region = GetRegionString(region_code); + + return info; + }(); + return s_info; + } + + u32 GetThrottleTimeWindowSecondsImpl() { + u32 seconds = DefaultThrottleTimeWindowSeconds; + if (settings::fwdbg::GetSettingsItemValue(std::addressof(seconds), sizeof(seconds), "erpt", "throttle_time_window_seconds") != sizeof(seconds)) { + return DefaultThrottleTimeWindowSeconds; + } + return seconds; + } + + void SetReportThrottleTimeSpan() { + u32 seconds = GetThrottleTimeWindowSecondsImpl(); + + const TimeSpan time_span = TimeSpan::FromSeconds(static_cast(seconds)); + + Reporter::SetThrottleTimeSpan(time_span); + } + Result Initialize(u8 *mem, size_t mem_size) { R_ABORT_UNLESS(time::Initialize()); @@ -103,6 +171,10 @@ namespace ams::erpt::srv { } } + if (hos::GetVersion() >= hos::Version_22_0_0) { + SetReportThrottleTimeSpan(); + } + R_ABORT_UNLESS(MountSystemSaveData()); g_sf_allocator.Attach(g_heap_handle); @@ -112,8 +184,18 @@ namespace ams::erpt::srv { AMS_ABORT_UNLESS(ctx != nullptr); } - if (R_FAILED(Journal::Restore())) { - /* TODO: Nintendo deletes system savedata when this fails. Should we?. */ + if (hos::GetVersion() >= hos::Version_21_0_0) { + /* >= 21.0.0, Nintendo checks the result of restore and deletes the save data if it fails. */ + if (R_FAILED(Journal::Restore())) { + /* Delete and recreate the system save data. */ + fs::Unmount(ReportStoragePath); + R_ABORT_UNLESS(fs::DeleteSystemSaveData(fs::SaveDataSpaceId::System, SystemSaveDataId, fs::InvalidUserId)); + + R_ABORT_UNLESS(MountSystemSaveData()); + } + } else{ + /* Pre 21.0.0, Nintendo just calls restore and ignores the result. */ + Journal::Restore(); } Reporter::UpdatePowerOnTime(); @@ -130,8 +212,8 @@ namespace ams::erpt::srv { R_RETURN(InitializeService()); } - Result SetSerialNumberAndOsVersion(const char *sn, u32 sn_len, const char *os, u32 os_len, const char *os_priv, u32 os_priv_len) { - R_RETURN(Reporter::SetSerialNumberAndOsVersion(sn, sn_len, os, os_len, os_priv, os_priv_len)); + Result SetSerialNumber(const char *sn, u32 sn_len) { + R_RETURN(Reporter::SetSerialNumber(sn, sn_len)); } Result SetProductModel(const char *model, u32 model_len) { diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_manager_impl.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_manager_impl.cpp index 668610ba0..62f072e86 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_manager_impl.cpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_manager_impl.cpp @@ -16,6 +16,7 @@ #include #include "erpt_srv_manager_impl.hpp" #include "erpt_srv_journal.hpp" +#include "erpt_srv_recent_report.hpp" namespace ams::erpt::srv { @@ -59,6 +60,7 @@ namespace ams::erpt::srv { Result ManagerImpl::CleanupReports() { Journal::CleanupReports(); Journal::CleanupAttachments(); + RecentReport::Clear(); R_RETURN(Journal::Commit()); } @@ -99,9 +101,15 @@ namespace ams::erpt::srv { Result ManagerImpl::GetReportSizeMax(ams::sf::Out out) { /* TODO: Where is this size defined? */ - constexpr size_t ReportSizeMax = 0x3FF4F; + constexpr size_t ReportSizeMax = 0x35D3D; *out = ReportSizeMax; R_SUCCEED(); } + Result ManagerImpl::GetRecentReportSummary(ams::sf::Out out) { + RecentReport::GetSummary(out.GetPointer()); + R_SUCCEED(); + } + + } diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_manager_impl.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_manager_impl.hpp index a4f55bb30..81b76ef82 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_manager_impl.hpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_manager_impl.hpp @@ -36,6 +36,7 @@ namespace ams::erpt::srv { Result GetStorageUsageStatistics(ams::sf::Out out); Result GetAttachmentListDeprecated(const ams::sf::OutBuffer &out_buf, const ReportId &report_id); Result GetAttachmentList(ams::sf::Out out_count, const ams::sf::OutBuffer &out_buf, const ReportId &report_id); + Result GetRecentReportSummary(ams::sf::Out out); Result GetReportSizeMax(ams::sf::Out out); }; static_assert(erpt::sf::IsIManager); diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_recent_report.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_recent_report.cpp new file mode 100644 index 000000000..ac3815185 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_recent_report.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "erpt_srv_recent_report.hpp" + +namespace ams::erpt::srv { + + namespace { + + constexpr size_t MaxEntriesPerType = 25; + + struct RecentReportState { + u32 report_counts[ReportType_Count]; + RecentReportEntry report_entries[ReportType_Count][MaxEntriesPerType]; + os::Tick last_tick; + u32 consecutive_count; + }; + + constinit RecentReportState g_state = { + .report_counts = {}, + .report_entries = {}, + .last_tick = os::Tick{}, + .consecutive_count = 0, + }; + + } + + void RecentReport::PushEntry(const char *error_code, const char *program_id, ReportType type, bool is_system_abort, bool is_application_abort) { + u32 &count = g_state.report_counts[type]; + RecentReportEntry *entries = g_state.report_entries[type]; + + /* If we're full, shift the oldest entry out. */ + if (count >= MaxEntriesPerType) { + std::memmove(entries, entries + 1, sizeof(RecentReportEntry) * (MaxEntriesPerType - 1)); + count = MaxEntriesPerType - 1; + } + + /* Fill the new entry. */ + RecentReportEntry &entry = entries[count]; + util::Strlcpy(entry.error_code, error_code, sizeof(entry.error_code)); + util::Strlcpy(entry.program_id, program_id, sizeof(entry.program_id)); + entry.is_visible = (type == ReportType_Visible); + entry.is_system_abort = is_system_abort; + entry.is_application_abort = is_application_abort; + + count++; + } + + void RecentReport::GetSummary(RecentReportSummary *out) { + /* Fill basic info from lazily-initialized system info. */ + const auto &sys_info = srv::GetSystemInfo(); + util::Strlcpy(out->firmware_display_version, sys_info.os_version, sizeof(out->firmware_display_version)); + util::Strlcpy(out->private_os_version, sys_info.private_os_version, sizeof(out->private_os_version)); + util::Strlcpy(out->product_model, sys_info.product_model, sizeof(out->product_model)); + util::Strlcpy(out->region_code, sys_info.region, sizeof(out->region_code)); + + u32 total_count = 0; + + /* Copy entries. */ + for (u32 i = 0; i < ReportType_Count; i++) { + if (g_state.report_counts[i] == 0) { + continue; + } + std::memcpy(out->entries + total_count, g_state.report_entries[i], sizeof(RecentReportEntry) * g_state.report_counts[i]); + total_count += g_state.report_counts[i]; + + /* Reset count (destructive read). */ + g_state.report_counts[i] = 0; + } + + out->entry_count = total_count; + } + + void RecentReport::Clear() { + for (u32 i = 0; i < ReportType_Count; i++) { + g_state.report_counts[i] = 0; + } + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_recent_report.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_recent_report.hpp new file mode 100644 index 000000000..065f82250 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_recent_report.hpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::erpt::srv { + + class RecentReport { + public: + static void PushEntry(const char *error_code, const char *program_id, ReportType type, bool is_system_abort, bool is_application_abort); + static void GetSummary(RecentReportSummary *out); + static void Clear(); + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.cpp index 995ffe30b..0ef5e2f61 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.cpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.cpp @@ -20,13 +20,12 @@ #include "erpt_srv_context_record.hpp" #include "erpt_srv_context.hpp" #include "erpt_srv_fs_info.hpp" +#include "erpt_srv_recent_report.hpp" namespace ams::erpt::srv { constinit bool Reporter::s_redirect_new_reports = true; constinit char Reporter::s_serial_number[24] = "Unknown"; - constinit char Reporter::s_os_version[24] = "Unknown"; - constinit char Reporter::s_private_os_version[96] = "Unknown"; constinit util::optional Reporter::s_application_launch_time; constinit util::optional Reporter::s_awake_time; constinit util::optional Reporter::s_power_on_time; @@ -212,18 +211,83 @@ namespace ams::erpt::srv { } #endif - Result ValidateCreateReportContext(const ContextEntry *ctx) { + Result ValidateAndGetErrorCode(const ContextEntry *ctx, char *out_error_code) { R_UNLESS(ctx->category == CategoryId_ErrorInfo, erpt::ResultRequiredContextMissing()); R_UNLESS(ctx->field_count <= FieldsPerContext, erpt::ResultInvalidArgument()); - const bool found_error_code = util::range::any_of(MakeSpan(ctx->fields, ctx->field_count), [] (const FieldEntry &entry) { - return entry.id == FieldId_ErrorCode; - }); - R_UNLESS(found_error_code, erpt::ResultRequiredFieldMissing()); + const auto fields_span = MakeSpan(ctx->fields, ctx->field_count); + const u8 *array_data = static_cast(ctx->array_buffer); + + const FieldEntry *error_code_field = nullptr; + + for (const auto &field : fields_span) { + if (field.id != FieldId_ErrorCode){ + continue; + } + error_code_field = &field; + break; + } + + R_UNLESS(error_code_field != nullptr, erpt::ResultRequiredFieldMissing()); + R_UNLESS(error_code_field->type == FieldType_String, erpt::ResultFieldTypeMismatch()); + R_UNLESS(error_code_field->value_array.size <= ErrorCodeSizeMax, erpt::ResultArrayFieldTooLarge()); + + const char *error_code = reinterpret_cast(array_data + error_code_field->value_array.start_idx); + util::Strlcpy(out_error_code, error_code, ErrorCodeSizeMax); R_SUCCEED(); } + namespace { + struct ThrottleState { + TimeSpan throttle_time_span; + char last_error_code[ErrorCodeSizeMax]; + u32 consecutive_count; + os::Tick last_tick; + }; + constinit ThrottleState g_throttle_state = { + .throttle_time_span = TimeSpan{}, + .last_error_code = {}, + .consecutive_count = 0, + .last_tick = os::Tick{}, + }; + }; + bool IsThrottledReport(const ContextEntry *ctx, ReportType type, const char *error_code) { + if (hos::GetVersion() < hos::Version_22_0_0) { + return false; + } + + const auto fields_span = MakeSpan(ctx->fields, ctx->field_count); + bool is_crash_report = false; + + for (const auto &field : fields_span) { + if (field.id != FieldId_CrashReportFlag){ + continue; + } + is_crash_report = field.value_bool; + break; + } + + if(type == ReportType_Visible || is_crash_report){ + return false; + } + + const auto now = os::GetSystemTick(); + const TimeSpan elapsed = (now - g_throttle_state.last_tick).ToTimeSpan(); + + if (std::strcmp(g_throttle_state.last_error_code, error_code) == 0 && elapsed < g_throttle_state.throttle_time_span) { + if (g_throttle_state.consecutive_count >= 5) { + return true; + } + g_throttle_state.consecutive_count++; + } else { + util::Strlcpy(g_throttle_state.last_error_code, error_code, sizeof(g_throttle_state.last_error_code)); + g_throttle_state.last_tick = now; + g_throttle_state.consecutive_count = 1; + } + + return false; + } Result SubmitReportDefaults(const ContextEntry *ctx) { AMS_ASSERT(ctx->category == CategoryId_ErrorInfo); @@ -381,6 +445,9 @@ namespace ams::erpt::srv { } + void Reporter::SetThrottleTimeSpan(TimeSpan time_span) { + g_throttle_state.throttle_time_span = time_span; + } Result Reporter::RegisterRunningApplet(ncm::ProgramId program_id) { g_applet_active_time_info_list.Register(program_id); R_SUCCEED(); @@ -427,12 +494,51 @@ namespace ams::erpt::srv { /* Get the context entry pointer. */ const ContextEntry *ctx = record->GetContextEntryPtr(); - /* Validate the context. */ - R_TRY(ValidateCreateReportContext(ctx)); + /* Validate the context and retrieve the error code. */ + char error_code[ErrorCodeSizeMax]; + R_TRY(ValidateAndGetErrorCode(ctx, error_code)); + + if (hos::GetVersion() >= hos::Version_22_0_0) { + /* Check if we should throttle the report. */ + if (IsThrottledReport(ctx, type, error_code)) { + R_SUCCEED(); + } + } /* Submit report defaults. */ R_TRY(SubmitReportDefaults(ctx)); + /* Push to recent reports. */ + if (hos::GetVersion() >= hos::Version_22_0_0) { + const auto fields_span = MakeSpan(ctx->fields, ctx->field_count); + const u8 *array_data = static_cast(ctx->array_buffer); + + char program_id[ProgramIdSizeMax] = {}; + bool is_system_abort = false; + bool is_application_abort = false; + + for (const auto &field : fields_span) { + switch (field.id) { + case FieldId_ProgramId: + if(field.type != FieldType_String){ + break; + } + util::Strlcpy(program_id, reinterpret_cast(array_data + field.value_array.start_idx), sizeof(program_id)); + break; + case FieldId_SystemAbortFlag: + is_system_abort = field.value_bool; + break; + case FieldId_ApplicationAbortFlag: + is_application_abort = field.value_bool; + break; + default: + break; + } + } + + RecentReport::PushEntry(error_code, program_id, type, is_system_abort, is_application_abort); + } + /* Generate report id. */ const ReportId report_id = specified_report_id ? *specified_report_id : ReportId{ .uuid = util::GenerateUuid() }; @@ -480,8 +586,9 @@ namespace ams::erpt::srv { R_ABORT_UNLESS(time::GetStandardSteadyClockCurrentTimePoint(std::addressof(steady_clock_current_timepoint))); /* Add automatic fields. */ - static_cast(auto_record->Add(FieldId_OsVersion, s_os_version, util::Strnlen(s_os_version, sizeof(s_os_version)))); - static_cast(auto_record->Add(FieldId_PrivateOsVersion, s_private_os_version, util::Strnlen(s_private_os_version, sizeof(s_private_os_version)))); + const auto &sys_info = srv::GetSystemInfo(); + static_cast(auto_record->Add(FieldId_OsVersion, sys_info.os_version, util::Strnlen(sys_info.os_version, sizeof(sys_info.os_version)))); + static_cast(auto_record->Add(FieldId_PrivateOsVersion, sys_info.private_os_version, util::Strnlen(sys_info.private_os_version, sizeof(sys_info.private_os_version)))); static_cast(auto_record->Add(FieldId_SerialNumber, s_serial_number, util::Strnlen(s_serial_number, sizeof(s_serial_number)))); static_cast(auto_record->Add(FieldId_ReportIdentifier, identifier_str, util::Strnlen(identifier_str, sizeof(identifier_str)))); static_cast(auto_record->Add(FieldId_OccurrenceTimestamp, timestamp_user.value)); diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.hpp index 48660542f..324c17c19 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.hpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.hpp @@ -23,8 +23,6 @@ namespace ams::erpt::srv { private: static bool s_redirect_new_reports; static char s_serial_number[24]; - static char s_os_version[24]; - static char s_private_os_version[96]; static util::optional s_application_launch_time; static util::optional s_awake_time; static util::optional s_power_on_time; @@ -39,14 +37,11 @@ namespace ams::erpt::srv { static void UpdateAwakeTime() { s_awake_time = os::GetSystemTick(); } static void UpdatePowerOnTime() { s_power_on_time = os::GetSystemTick(); } - static Result SetSerialNumberAndOsVersion(const char *sn, u32 sn_len, const char *os, u32 os_len, const char *os_priv, u32 os_priv_len) { - R_UNLESS(sn_len <= sizeof(s_serial_number), erpt::ResultInvalidArgument()); - R_UNLESS(os_len <= sizeof(s_os_version), erpt::ResultInvalidArgument()); - R_UNLESS(os_priv_len <= sizeof(s_private_os_version), erpt::ResultInvalidArgument()); + static void SetThrottleTimeSpan(TimeSpan time_span); + static Result SetSerialNumber(const char *sn, u32 sn_len) { + R_UNLESS(sn_len <= sizeof(s_serial_number), erpt::ResultInvalidArgument()); std::memcpy(s_serial_number, sn, sn_len); - std::memcpy(s_os_version, os, os_len); - std::memcpy(s_private_os_version, os_priv, os_priv_len); R_SUCCEED(); } diff --git a/libraries/libvapours/include/vapours/results/erpt_results.hpp b/libraries/libvapours/include/vapours/results/erpt_results.hpp index 10c1b9aa9..093f2f25f 100644 --- a/libraries/libvapours/include/vapours/results/erpt_results.hpp +++ b/libraries/libvapours/include/vapours/results/erpt_results.hpp @@ -40,5 +40,6 @@ namespace ams::erpt { R_DEFINE_ERROR_RESULT(InvalidPowerState, 17); R_DEFINE_ERROR_RESULT(ArrayFieldTooLarge, 18); R_DEFINE_ERROR_RESULT(AlreadyOwned, 19); + R_DEFINE_ERROR_RESULT(TooManyOutAttachments, 51); } diff --git a/stratosphere/erpt/source/erpt_main.cpp b/stratosphere/erpt/source/erpt_main.cpp index d95a0ae60..93096dbcb 100644 --- a/stratosphere/erpt/source/erpt_main.cpp +++ b/stratosphere/erpt/source/erpt_main.cpp @@ -26,26 +26,6 @@ namespace ams { } - int MakeProductModelString(char *dst, size_t dst_size, settings::system::ProductModel model) { - switch (model) { - case settings::system::ProductModel_Invalid: return util::Strlcpy(dst, "Invalid", static_cast(dst_size)); - case settings::system::ProductModel_Nx: return util::Strlcpy(dst, "NX", static_cast(dst_size)); - default: return util::SNPrintf(dst, dst_size, "%d", static_cast(model)); - } - } - - const char *GetRegionString(settings::system::RegionCode code) { - switch (code) { - case settings::system::RegionCode_Japan: return "Japan"; - case settings::system::RegionCode_Usa: return "Usa"; - case settings::system::RegionCode_Europe: return "Europe"; - case settings::system::RegionCode_Australia: return "Australia"; - case settings::system::RegionCode_HongKongTaiwanKorea: return "HongKongTaiwanKorea"; - case settings::system::RegionCode_China: return "China"; - default: return "RegionUnknown"; - } - } - } namespace init { @@ -94,42 +74,19 @@ namespace ams { /* Atmosphere always wants to redirect new reports to the SD card, to prevent them from being logged. */ erpt::srv::SetRedirectNewReportsToSdCard(true); - /* Configure the OS version. */ + /* Configure the serial number, OS version, product model, and region. */ { - settings::system::FirmwareVersion firmware_version = {}; + const auto &sys_info = erpt::srv::GetSystemInfo(); + settings::system::SerialNumber serial_number = {}; - settings::system::GetFirmwareVersion(std::addressof(firmware_version)); settings::system::GetSerialNumber(std::addressof(serial_number)); - char os_private[0x60]; - const auto os_priv_len = util::SNPrintf(os_private, sizeof(os_private), "%s (%.8s)", firmware_version.display_name, firmware_version.revision); - AMS_ASSERT(static_cast(os_priv_len) < sizeof(os_private)); - AMS_UNUSED(os_priv_len); + R_ABORT_UNLESS(erpt::srv::SetSerialNumber(serial_number.str, + strnlen(serial_number.str, sizeof(serial_number.str) - 1) + 1)); - R_ABORT_UNLESS(erpt::srv::SetSerialNumberAndOsVersion(serial_number.str, - strnlen(serial_number.str, sizeof(serial_number.str) - 1) + 1, - firmware_version.display_version, - strnlen(firmware_version.display_version, sizeof(firmware_version.display_version) - 1) + 1, - os_private, - strnlen(os_private, sizeof(os_private) - 1) + 1)); - } + R_ABORT_UNLESS(erpt::srv::SetProductModel(sys_info.product_model, static_cast(std::strlen(sys_info.product_model)))); - /* Configure the product model. */ - { - char product_model[0x10]; - const auto pm_len = erpt::MakeProductModelString(product_model, sizeof(product_model), settings::system::GetProductModel()); - AMS_ASSERT(static_cast(pm_len) < sizeof(product_model)); - AMS_UNUSED(pm_len); - - R_ABORT_UNLESS(erpt::srv::SetProductModel(product_model, static_cast(std::strlen(product_model)))); - } - - /* Configure the region. */ - { - settings::system::RegionCode code; - settings::system::GetRegionCode(std::addressof(code)); - const char *region_str = erpt::GetRegionString(code); - R_ABORT_UNLESS(erpt::srv::SetRegionSetting(region_str, static_cast(std::strlen(region_str)))); + R_ABORT_UNLESS(erpt::srv::SetRegionSetting(sys_info.region, static_cast(std::strlen(sys_info.region)))); } /* Start the erpt server. */