erpt: Implement 22.0.0 commands and changes
Co-authored-by: nvnprogram <97150065+nvnprogram@users.noreply.github.com>
This commit is contained in:
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, GetStorageUsageStatistics, (ams::sf::Out<erpt::StorageUsageStatistics> 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<u32> 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<erpt::RecentReportSummary> out), (out), hos::Version_22_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 10, Result, GetReportSizeMax, (ams::sf::Out<u32> out), (out), hos::Version_20_0_0)
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<int>(dst_size));
|
||||
case settings::system::ProductModel_Nx: return util::Strlcpy(dst, "NX", static_cast<int>(dst_size));
|
||||
default: return util::SNPrintf(dst, dst_size, "%d", static_cast<int>(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<size_t>(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<size_t>(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<s64>(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) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <stratosphere.hpp>
|
||||
#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<u32> 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<RecentReportSummary> out) {
|
||||
RecentReport::GetSummary(out.GetPointer());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace ams::erpt::srv {
|
||||
Result GetStorageUsageStatistics(ams::sf::Out<StorageUsageStatistics> out);
|
||||
Result GetAttachmentListDeprecated(const ams::sf::OutBuffer &out_buf, const ReportId &report_id);
|
||||
Result GetAttachmentList(ams::sf::Out<u32> out_count, const ams::sf::OutBuffer &out_buf, const ReportId &report_id);
|
||||
Result GetRecentReportSummary(ams::sf::Out<RecentReportSummary> out);
|
||||
Result GetReportSizeMax(ams::sf::Out<u32> out);
|
||||
};
|
||||
static_assert(erpt::sf::IsIManager<ManagerImpl>);
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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<os::Tick> Reporter::s_application_launch_time;
|
||||
constinit util::optional<os::Tick> Reporter::s_awake_time;
|
||||
constinit util::optional<os::Tick> 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<const u8 *>(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<const char *>(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<const u8 *>(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<const char *>(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<void>(auto_record->Add(FieldId_OsVersion, s_os_version, util::Strnlen(s_os_version, sizeof(s_os_version))));
|
||||
static_cast<void>(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<void>(auto_record->Add(FieldId_OsVersion, sys_info.os_version, util::Strnlen(sys_info.os_version, sizeof(sys_info.os_version))));
|
||||
static_cast<void>(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<void>(auto_record->Add(FieldId_SerialNumber, s_serial_number, util::Strnlen(s_serial_number, sizeof(s_serial_number))));
|
||||
static_cast<void>(auto_record->Add(FieldId_ReportIdentifier, identifier_str, util::Strnlen(identifier_str, sizeof(identifier_str))));
|
||||
static_cast<void>(auto_record->Add(FieldId_OccurrenceTimestamp, timestamp_user.value));
|
||||
|
||||
@@ -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<os::Tick> s_application_launch_time;
|
||||
static util::optional<os::Tick> s_awake_time;
|
||||
static util::optional<os::Tick> 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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<int>(dst_size));
|
||||
case settings::system::ProductModel_Nx: return util::Strlcpy(dst, "NX", static_cast<int>(dst_size));
|
||||
default: return util::SNPrintf(dst, dst_size, "%d", static_cast<int>(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<size_t>(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<u32>(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<size_t>(pm_len) < sizeof(product_model));
|
||||
AMS_UNUSED(pm_len);
|
||||
|
||||
R_ABORT_UNLESS(erpt::srv::SetProductModel(product_model, static_cast<u32>(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<u32>(std::strlen(region_str))));
|
||||
R_ABORT_UNLESS(erpt::srv::SetRegionSetting(sys_info.region, static_cast<u32>(std::strlen(sys_info.region))));
|
||||
}
|
||||
|
||||
/* Start the erpt server. */
|
||||
|
||||
Reference in New Issue
Block a user