mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-12-18 08:22:04 +00:00
erpt: implement forced shutdown detection
This commit is contained in:
parent
ef0c15b764
commit
355010ad84
17 changed files with 645 additions and 53 deletions
|
@ -24,6 +24,7 @@ namespace ams::erpt::srv {
|
||||||
|
|
||||||
constexpr inline const char ReportStoragePath[] = "save";
|
constexpr inline const char ReportStoragePath[] = "save";
|
||||||
constexpr inline const char JournalFileName[] = "save:/journal";
|
constexpr inline const char JournalFileName[] = "save:/journal";
|
||||||
|
constexpr inline const char ForcedShutdownContextFileName[] = "save:/forced-shutdown";
|
||||||
|
|
||||||
constexpr size_t ReportFileNameLength = 64;
|
constexpr size_t ReportFileNameLength = 64;
|
||||||
constexpr size_t AttachmentFileNameLength = 64;
|
constexpr size_t AttachmentFileNameLength = 64;
|
||||||
|
|
|
@ -16,4 +16,6 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <stratosphere/err/err_types.hpp>
|
||||||
#include <stratosphere/err/err_error_context.hpp>
|
#include <stratosphere/err/err_error_context.hpp>
|
||||||
|
#include <stratosphere/err/err_system_api.hpp>
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 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 <vapours.hpp>
|
||||||
|
#include <stratosphere/err/err_types.hpp>
|
||||||
|
|
||||||
|
namespace ams::err {
|
||||||
|
|
||||||
|
ErrorCode ConvertResultToErrorCode(const Result &result);
|
||||||
|
void GetErrorCodeString(char *dst, size_t dst_size, ErrorCode error_code);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 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 <vapours.hpp>
|
||||||
|
|
||||||
|
namespace ams::err {
|
||||||
|
|
||||||
|
using ErrorCodeCategory = u32;
|
||||||
|
using ErrorCodeNumber = u32;
|
||||||
|
|
||||||
|
struct ErrorCode {
|
||||||
|
static constexpr auto StringLengthMax = 15;
|
||||||
|
|
||||||
|
ErrorCodeCategory category;
|
||||||
|
ErrorCodeNumber number;
|
||||||
|
|
||||||
|
constexpr ALWAYS_INLINE bool IsValid() const { return this->category > 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr inline ErrorCode InvalidErrorCode = {
|
||||||
|
.category = 0,
|
||||||
|
.number = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -19,6 +19,7 @@
|
||||||
#include "erpt_srv_context.hpp"
|
#include "erpt_srv_context.hpp"
|
||||||
#include "erpt_srv_reporter.hpp"
|
#include "erpt_srv_reporter.hpp"
|
||||||
#include "erpt_srv_journal.hpp"
|
#include "erpt_srv_journal.hpp"
|
||||||
|
#include "erpt_srv_forced_shutdown.hpp"
|
||||||
|
|
||||||
namespace ams::erpt::srv {
|
namespace ams::erpt::srv {
|
||||||
|
|
||||||
|
@ -32,6 +33,8 @@ namespace ams::erpt::srv {
|
||||||
R_UNLESS(ctx_size == sizeof(ContextEntry), erpt::ResultInvalidArgument());
|
R_UNLESS(ctx_size == sizeof(ContextEntry), erpt::ResultInvalidArgument());
|
||||||
R_UNLESS(data_size <= ArrayBufferSizeMax, erpt::ResultInvalidArgument());
|
R_UNLESS(data_size <= ArrayBufferSizeMax, erpt::ResultInvalidArgument());
|
||||||
|
|
||||||
|
SubmitContextForForcedShutdownDetection(ctx, data, data_size);
|
||||||
|
|
||||||
return Context::SubmitContext(ctx, data, data_size);
|
return Context::SubmitContext(ctx, data, data_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +174,8 @@ namespace ams::erpt::srv {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ContextImpl::InvalidateForcedShutdownDetection() {
|
Result ContextImpl::InvalidateForcedShutdownDetection() {
|
||||||
/* TODO: For greater accuracy, we should support the forced shutdown detection feature added in 12.0.0. */
|
/* NOTE: Nintendo does not check the result here. */
|
||||||
|
erpt::srv::InvalidateForcedShutdownDetection();
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,331 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 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_forced_shutdown.hpp"
|
||||||
|
#include "erpt_srv_context.hpp"
|
||||||
|
#include "erpt_srv_context_record.hpp"
|
||||||
|
#include "erpt_srv_reporter.hpp"
|
||||||
|
#include "erpt_srv_stream.hpp"
|
||||||
|
|
||||||
|
namespace ams::erpt::srv {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr u32 ForcedShutdownContextBufferSize = 1_KB;
|
||||||
|
|
||||||
|
constexpr u32 ForcedShutdownContextVersion = 1;
|
||||||
|
|
||||||
|
struct ForcedShutdownContextHeader {
|
||||||
|
u32 version;
|
||||||
|
u32 num_contexts;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(ForcedShutdownContextHeader) == 8);
|
||||||
|
|
||||||
|
struct ForcedShutdownContextEntry {
|
||||||
|
u32 version;
|
||||||
|
CategoryId category;
|
||||||
|
u32 field_count;
|
||||||
|
u32 array_buffer_size;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(ForcedShutdownContextEntry) == 16);
|
||||||
|
|
||||||
|
os::Event g_forced_shutdown_update_event(os::EventClearMode_ManualClear);
|
||||||
|
|
||||||
|
constinit ContextEntry g_forced_shutdown_contexts[] = {
|
||||||
|
{ .category = CategoryId_RunningApplicationInfo, },
|
||||||
|
{ .category = CategoryId_RunningAppletInfo, },
|
||||||
|
{ .category = CategoryId_FocusedAppletHistoryInfo, },
|
||||||
|
};
|
||||||
|
|
||||||
|
bool IsForceShutdownDetected() {
|
||||||
|
fs::DirectoryEntryType entry_type;
|
||||||
|
return R_SUCCEEDED(fs::GetEntryType(std::addressof(entry_type), ForcedShutdownContextFileName));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CreateForcedShutdownContext() {
|
||||||
|
/* Create the context. */
|
||||||
|
{
|
||||||
|
/* Create the stream. */
|
||||||
|
Stream stream;
|
||||||
|
R_TRY(stream.OpenStream(ForcedShutdownContextFileName, StreamMode_Write, 0));
|
||||||
|
|
||||||
|
/* Write a context header. */
|
||||||
|
const ForcedShutdownContextHeader header = { .version = ForcedShutdownContextVersion, .num_contexts = 0, };
|
||||||
|
R_TRY(stream.WriteStream(reinterpret_cast<const u8 *>(std::addressof(header)), sizeof(header)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Commit the context. */
|
||||||
|
R_TRY(Stream::CommitStream());
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CreateReportForForcedShutdown() {
|
||||||
|
/* Create a new context record. */
|
||||||
|
/* NOTE: Nintendo does not check that this allocation succeeds. */
|
||||||
|
auto record = std::make_unique<ContextRecord>(CategoryId_ErrorInfo);
|
||||||
|
|
||||||
|
/* Create error code for the report. */
|
||||||
|
char error_code_str[err::ErrorCode::StringLengthMax];
|
||||||
|
err::GetErrorCodeString(error_code_str, sizeof(error_code_str), err::ConvertResultToErrorCode(err::ResultForcedShutdownDetected()));
|
||||||
|
|
||||||
|
/* Add error code to the context. */
|
||||||
|
R_TRY(record->Add(FieldId_ErrorCode, error_code_str, std::strlen(error_code_str)));
|
||||||
|
|
||||||
|
/* Create report. */
|
||||||
|
R_TRY(Reporter::CreateReport(ReportType_Invisible, ResultSuccess(), std::move(record), nullptr, nullptr, 0));
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result LoadForcedShutdownContext() {
|
||||||
|
/* Create the stream to read the context. */
|
||||||
|
Stream stream;
|
||||||
|
R_TRY(stream.OpenStream(ForcedShutdownContextFileName, StreamMode_Read, ForcedShutdownContextBufferSize));
|
||||||
|
|
||||||
|
/* Read the header. */
|
||||||
|
u32 read_size;
|
||||||
|
ForcedShutdownContextHeader header;
|
||||||
|
R_TRY(stream.ReadStream(std::addressof(read_size), reinterpret_cast<u8 *>(std::addressof(header)), sizeof(header)));
|
||||||
|
|
||||||
|
/* Validate the header. */
|
||||||
|
R_SUCCEED_IF(read_size != sizeof(header));
|
||||||
|
R_SUCCEED_IF(ForcedShutdownContextVersion);
|
||||||
|
R_SUCCEED_IF(header.num_contexts == 0);
|
||||||
|
|
||||||
|
/* Read out the contexts. */
|
||||||
|
for (u32 i = 0; i < header.num_contexts; ++i) {
|
||||||
|
/* Read the context entry header. */
|
||||||
|
ForcedShutdownContextEntry entry_header;
|
||||||
|
R_TRY(stream.ReadStream(std::addressof(read_size), reinterpret_cast<u8 *>(std::addressof(entry_header)), sizeof(entry_header)));
|
||||||
|
|
||||||
|
if (read_size != sizeof(entry_header)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry_header.field_count == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read the saved data into a context entry. */
|
||||||
|
ContextEntry ctx = {
|
||||||
|
.version = entry_header.version,
|
||||||
|
.field_count = entry_header.field_count,
|
||||||
|
.category = entry_header.category,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Check that the field count is valid. */
|
||||||
|
AMS_ABORT_UNLESS(entry_header.field_count <= util::size(ctx.fields));
|
||||||
|
|
||||||
|
/* Read the fields. */
|
||||||
|
R_TRY(stream.ReadStream(std::addressof(read_size), reinterpret_cast<u8 *>(std::addressof(ctx.fields)), entry_header.field_count * sizeof(ctx.fields[0])));
|
||||||
|
if (read_size != entry_header.field_count * sizeof(ctx.fields[0])) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate an array buffer. */
|
||||||
|
u8 *array_buffer = static_cast<u8 *>(Allocate(entry_header.array_buffer_size));
|
||||||
|
if (array_buffer == nullptr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ON_SCOPE_EXIT { Deallocate(array_buffer); };
|
||||||
|
|
||||||
|
/* Read the array buffer data. */
|
||||||
|
R_TRY(stream.ReadStream(std::addressof(read_size), array_buffer, entry_header.array_buffer_size));
|
||||||
|
if (read_size != entry_header.array_buffer_size) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create a record for the context. */
|
||||||
|
auto record = std::make_unique<ContextRecord>();
|
||||||
|
if (record == nullptr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the record. */
|
||||||
|
R_TRY(record->Initialize(std::addressof(ctx), array_buffer, entry_header.array_buffer_size));
|
||||||
|
|
||||||
|
/* Submit the record. */
|
||||||
|
R_TRY(Context::SubmitContextRecord(std::move(record)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetForcedShutdownContextCount() {
|
||||||
|
u32 count = 0;
|
||||||
|
for (const auto &ctx : g_forced_shutdown_contexts) {
|
||||||
|
if (ctx.field_count != 0) {
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SaveForcedShutdownContextImpl() {
|
||||||
|
/* Save context to file. */
|
||||||
|
{
|
||||||
|
/* Create the stream to write the context. */
|
||||||
|
Stream stream;
|
||||||
|
R_TRY(stream.OpenStream(ForcedShutdownContextFileName, StreamMode_Write, ForcedShutdownContextBufferSize));
|
||||||
|
|
||||||
|
/* Write a context header. */
|
||||||
|
const ForcedShutdownContextHeader header = { .version = ForcedShutdownContextVersion, .num_contexts = GetForcedShutdownContextCount(), };
|
||||||
|
R_TRY(stream.WriteStream(reinterpret_cast<const u8 *>(std::addressof(header)), sizeof(header)));
|
||||||
|
|
||||||
|
/* Write each context. */
|
||||||
|
for (const auto &ctx : g_forced_shutdown_contexts) {
|
||||||
|
/* If the context has no fields, continue. */
|
||||||
|
if (ctx.field_count == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write a context entry header. */
|
||||||
|
const ForcedShutdownContextEntry entry_header = {
|
||||||
|
.version = ctx.version,
|
||||||
|
.category = ctx.category,
|
||||||
|
.field_count = ctx.field_count,
|
||||||
|
.array_buffer_size = ctx.array_buffer_size,
|
||||||
|
};
|
||||||
|
R_TRY(stream.WriteStream(reinterpret_cast<const u8 *>(std::addressof(entry_header)), sizeof(entry_header)));
|
||||||
|
|
||||||
|
/* Write all fields. */
|
||||||
|
for (u32 i = 0; i < ctx.field_count; ++i) {
|
||||||
|
R_TRY(stream.WriteStream(reinterpret_cast<const u8 *>(ctx.fields + i), sizeof(ctx.fields[0])));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write the array buffer. */
|
||||||
|
R_TRY(stream.WriteStream(ctx.array_buffer, ctx.array_buffer_size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Commit the context. */
|
||||||
|
R_TRY(Stream::CommitStream());
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
os::Event *GetForcedShutdownUpdateEvent() {
|
||||||
|
return std::addressof(g_forced_shutdown_update_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeForcedShutdownDetection() {
|
||||||
|
/* Check if the forced shutdown context exists; if it doesn't, we should create an empty one. */
|
||||||
|
if (!IsForceShutdownDetected()) {
|
||||||
|
/* NOTE: Nintendo does not check result here. */
|
||||||
|
CreateForcedShutdownContext();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load the forced shutdown context. */
|
||||||
|
/* NOTE: Nintendo does not check that this succeeds. */
|
||||||
|
LoadForcedShutdownContext();
|
||||||
|
|
||||||
|
/* Create report for the forced shutdown. */
|
||||||
|
/* NOTE: Nintendo does not check that this succeeds. */
|
||||||
|
CreateReportForForcedShutdown();
|
||||||
|
|
||||||
|
/* Clear the forced shutdown categories. */
|
||||||
|
/* NOTE: Nintendo does not check that this succeeds. */
|
||||||
|
Context::ClearContext(CategoryId_RunningApplicationInfo);
|
||||||
|
Context::ClearContext(CategoryId_RunningAppletInfo);
|
||||||
|
Context::ClearContext(CategoryId_FocusedAppletHistoryInfo);
|
||||||
|
|
||||||
|
/* Save the forced shutdown context. */
|
||||||
|
/* NOTE: Nintendo does not check that this succeeds. */
|
||||||
|
SaveForcedShutdownContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FinalizeForcedShutdownDetection() {
|
||||||
|
/* Try to delete the context. */
|
||||||
|
const Result result = Stream::DeleteStream(ForcedShutdownContextFileName);
|
||||||
|
if (!fs::ResultPathNotFound::Includes(result)) {
|
||||||
|
/* We must have succeeded, if the file existed. */
|
||||||
|
R_ABORT_UNLESS(result);
|
||||||
|
|
||||||
|
/* Commit the deletion. */
|
||||||
|
R_ABORT_UNLESS(Stream::CommitStream());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveForcedShutdownContext() {
|
||||||
|
/* NOTE: Nintendo does not check that saving the report succeeds. */
|
||||||
|
SaveForcedShutdownContextImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SubmitContextForForcedShutdownDetection(const ContextEntry *entry, const u8 *data, u32 data_size) {
|
||||||
|
/* If the context entry matches one of our tracked categories, update our stored category. */
|
||||||
|
for (auto &ctx : g_forced_shutdown_contexts) {
|
||||||
|
/* Check for a match. */
|
||||||
|
if (ctx.category != entry->category) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we have an existing array buffer, free it. */
|
||||||
|
if (ctx.array_buffer != nullptr) {
|
||||||
|
Deallocate(ctx.array_buffer);
|
||||||
|
ctx.array_buffer = nullptr;
|
||||||
|
ctx.array_buffer_size = 0;
|
||||||
|
ctx.array_free_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy in the context. */
|
||||||
|
ctx = *entry;
|
||||||
|
|
||||||
|
/* Add the submitted data. */
|
||||||
|
if (data != nullptr && data_size > 0) {
|
||||||
|
/* Allocate new array buffer. */
|
||||||
|
ctx.array_buffer = static_cast<u8 *>(Allocate(data_size));
|
||||||
|
if (ctx.array_buffer == nullptr) {
|
||||||
|
/* We failed to allocate; this is okay, but clear our field count. */
|
||||||
|
ctx.field_count = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy in the data. */
|
||||||
|
std::memcpy(ctx.array_buffer, data, data_size);
|
||||||
|
|
||||||
|
/* Set buffer extents. */
|
||||||
|
ctx.array_buffer_size = data_size;
|
||||||
|
ctx.array_free_count = 0;
|
||||||
|
} else {
|
||||||
|
ctx.array_buffer = nullptr;
|
||||||
|
ctx.array_buffer_size = 0;
|
||||||
|
ctx.array_free_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Signal, to notify that we had an update. */
|
||||||
|
g_forced_shutdown_update_event.Signal();
|
||||||
|
|
||||||
|
/* We're done processing, since we found a match. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InvalidateForcedShutdownDetection() {
|
||||||
|
/* Delete the forced shutdown context. */
|
||||||
|
R_TRY(Stream::DeleteStream(ForcedShutdownContextFileName));
|
||||||
|
|
||||||
|
/* Commit the deletion. */
|
||||||
|
R_TRY(Stream::CommitStream());
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 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 {
|
||||||
|
|
||||||
|
os::Event *GetForcedShutdownUpdateEvent();
|
||||||
|
|
||||||
|
void InitializeForcedShutdownDetection();
|
||||||
|
void FinalizeForcedShutdownDetection();
|
||||||
|
|
||||||
|
void SaveForcedShutdownContext();
|
||||||
|
|
||||||
|
void SubmitContextForForcedShutdownDetection(const ContextEntry *entry, const u8 *data, u32 data_size);
|
||||||
|
|
||||||
|
Result InvalidateForcedShutdownDetection();
|
||||||
|
|
||||||
|
}
|
|
@ -20,6 +20,7 @@
|
||||||
#include "erpt_srv_reporter.hpp"
|
#include "erpt_srv_reporter.hpp"
|
||||||
#include "erpt_srv_journal.hpp"
|
#include "erpt_srv_journal.hpp"
|
||||||
#include "erpt_srv_service.hpp"
|
#include "erpt_srv_service.hpp"
|
||||||
|
#include "erpt_srv_forced_shutdown.hpp"
|
||||||
|
|
||||||
namespace ams::erpt::srv {
|
namespace ams::erpt::srv {
|
||||||
|
|
||||||
|
@ -96,6 +97,10 @@ namespace ams::erpt::srv {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result InitializeAndStartService() {
|
Result InitializeAndStartService() {
|
||||||
|
/* Initialize forced shutdown detection. */
|
||||||
|
/* NOTE: Nintendo does not check error code here. */
|
||||||
|
InitializeForcedShutdownDetection();
|
||||||
|
|
||||||
return InitializeService();
|
return InitializeService();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +136,15 @@ namespace ams::erpt::srv {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Wait() {
|
void Wait() {
|
||||||
return WaitService();
|
/* Get the update event. */
|
||||||
|
os::Event *event = GetForcedShutdownUpdateEvent();
|
||||||
|
|
||||||
|
/* Forever wait, saving any updates. */
|
||||||
|
while (true) {
|
||||||
|
event->Wait();
|
||||||
|
event->Clear();
|
||||||
|
SaveForcedShutdownContext();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "erpt_srv_context_impl.hpp"
|
#include "erpt_srv_context_impl.hpp"
|
||||||
#include "erpt_srv_session_impl.hpp"
|
#include "erpt_srv_session_impl.hpp"
|
||||||
#include "erpt_srv_stream.hpp"
|
#include "erpt_srv_stream.hpp"
|
||||||
|
#include "erpt_srv_forced_shutdown.hpp"
|
||||||
|
|
||||||
namespace ams::erpt::srv {
|
namespace ams::erpt::srv {
|
||||||
|
|
||||||
|
@ -117,8 +118,10 @@ namespace ams::erpt::srv {
|
||||||
case psc::PmState_ReadyAwaken:
|
case psc::PmState_ReadyAwaken:
|
||||||
Stream::EnableFsAccess(true);
|
Stream::EnableFsAccess(true);
|
||||||
break;
|
break;
|
||||||
case psc::PmState_ReadySleep:
|
|
||||||
case psc::PmState_ReadyShutdown:
|
case psc::PmState_ReadyShutdown:
|
||||||
|
FinalizeForcedShutdownDetection();
|
||||||
|
[[fallthrough]];
|
||||||
|
case psc::PmState_ReadySleep:
|
||||||
Stream::EnableFsAccess(false);
|
Stream::EnableFsAccess(false);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -19,7 +19,8 @@
|
||||||
|
|
||||||
namespace ams::erpt::srv {
|
namespace ams::erpt::srv {
|
||||||
|
|
||||||
bool Stream::s_can_access_fs = true;
|
constinit bool Stream::s_can_access_fs = true;
|
||||||
|
constinit os::SdkMutex Stream::s_fs_commit_mutex;
|
||||||
|
|
||||||
void Stream::EnableFsAccess(bool en) {
|
void Stream::EnableFsAccess(bool en) {
|
||||||
s_can_access_fs = en;
|
s_can_access_fs = en;
|
||||||
|
@ -32,6 +33,9 @@ namespace ams::erpt::srv {
|
||||||
|
|
||||||
Result Stream::CommitStream() {
|
Result Stream::CommitStream() {
|
||||||
R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState());
|
R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState());
|
||||||
|
|
||||||
|
std::scoped_lock lk(s_fs_commit_mutex);
|
||||||
|
|
||||||
fs::CommitSaveData(ReportStoragePath);
|
fs::CommitSaveData(ReportStoragePath);
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
@ -52,13 +56,22 @@ namespace ams::erpt::srv {
|
||||||
|
|
||||||
Stream::~Stream() {
|
Stream::~Stream() {
|
||||||
this->CloseStream();
|
this->CloseStream();
|
||||||
|
AMS_ASSERT(!s_fs_commit_mutex.IsLockedByCurrentThread());
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Stream::OpenStream(const char *path, StreamMode mode, u32 buffer_size) {
|
Result Stream::OpenStream(const char *path, StreamMode mode, u32 buffer_size) {
|
||||||
R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState());
|
R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState());
|
||||||
R_UNLESS(!this->initialized, erpt::ResultAlreadyInitialized());
|
R_UNLESS(!this->initialized, erpt::ResultAlreadyInitialized());
|
||||||
|
|
||||||
|
auto lock_guard = SCOPE_GUARD {
|
||||||
|
if (s_fs_commit_mutex.IsLockedByCurrentThread()) {
|
||||||
|
s_fs_commit_mutex.Unlock();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (mode == StreamMode_Write) {
|
if (mode == StreamMode_Write) {
|
||||||
|
s_fs_commit_mutex.Lock();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
R_TRY_CATCH(fs::OpenFile(std::addressof(this->file_handle), path, fs::OpenMode_Write | fs::OpenMode_AllowAppend)) {
|
R_TRY_CATCH(fs::OpenFile(std::addressof(this->file_handle), path, fs::OpenMode_Write | fs::OpenMode_AllowAppend)) {
|
||||||
R_CATCH(fs::ResultPathNotFound) {
|
R_CATCH(fs::ResultPathNotFound) {
|
||||||
|
@ -71,16 +84,23 @@ namespace ams::erpt::srv {
|
||||||
fs::SetFileSize(this->file_handle, 0);
|
fs::SetFileSize(this->file_handle, 0);
|
||||||
} else {
|
} else {
|
||||||
R_UNLESS(mode == StreamMode_Read, erpt::ResultInvalidArgument());
|
R_UNLESS(mode == StreamMode_Read, erpt::ResultInvalidArgument());
|
||||||
|
|
||||||
|
fs::OpenFile(std::addressof(this->file_handle), path, fs::OpenMode_Read);
|
||||||
}
|
}
|
||||||
auto file_guard = SCOPE_GUARD { if (mode == StreamMode_Write) { fs::CloseFile(this->file_handle); } };
|
auto file_guard = SCOPE_GUARD { fs::CloseFile(this->file_handle); };
|
||||||
|
|
||||||
std::strncpy(this->file_name, path, sizeof(this->file_name));
|
std::strncpy(this->file_name, path, sizeof(this->file_name));
|
||||||
this->file_name[sizeof(this->file_name) - 1] = '\x00';
|
this->file_name[sizeof(this->file_name) - 1] = '\x00';
|
||||||
|
|
||||||
|
if (buffer_size > 0) {
|
||||||
this->buffer = reinterpret_cast<u8 *>(Allocate(buffer_size));
|
this->buffer = reinterpret_cast<u8 *>(Allocate(buffer_size));
|
||||||
R_UNLESS(this->buffer != nullptr, erpt::ResultOutOfMemory());
|
AMS_ASSERT(this->buffer != nullptr);
|
||||||
|
} else {
|
||||||
|
this->buffer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
this->buffer_size = buffer_size;
|
|
||||||
|
this->buffer_size = this->buffer != nullptr ? buffer_size : 0;
|
||||||
this->buffer_count = 0;
|
this->buffer_count = 0;
|
||||||
this->buffer_position = 0;
|
this->buffer_position = 0;
|
||||||
this->file_position = 0;
|
this->file_position = 0;
|
||||||
|
@ -88,6 +108,7 @@ namespace ams::erpt::srv {
|
||||||
this->initialized = true;
|
this->initialized = true;
|
||||||
|
|
||||||
file_guard.Cancel();
|
file_guard.Cancel();
|
||||||
|
lock_guard.Cancel();
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,19 +119,14 @@ namespace ams::erpt::srv {
|
||||||
R_UNLESS(out != nullptr, erpt::ResultInvalidArgument());
|
R_UNLESS(out != nullptr, erpt::ResultInvalidArgument());
|
||||||
R_UNLESS(dst != nullptr, erpt::ResultInvalidArgument());
|
R_UNLESS(dst != nullptr, erpt::ResultInvalidArgument());
|
||||||
|
|
||||||
fs::FileHandle tmp_file;
|
|
||||||
size_t fs_read_size;
|
size_t fs_read_size;
|
||||||
u32 read_count = 0;
|
u32 read_count = 0;
|
||||||
bool opened = false;
|
|
||||||
|
|
||||||
ON_SCOPE_EXIT {
|
ON_SCOPE_EXIT {
|
||||||
*out = read_count;
|
*out = read_count;
|
||||||
|
|
||||||
if (opened) {
|
|
||||||
fs::CloseFile(tmp_file);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (this->buffer != nullptr) {
|
||||||
while (dst_size > 0) {
|
while (dst_size > 0) {
|
||||||
if (u32 cur = std::min<u32>(this->buffer_count - this->buffer_position, dst_size); cur > 0) {
|
if (u32 cur = std::min<u32>(this->buffer_count - this->buffer_position, dst_size); cur > 0) {
|
||||||
std::memcpy(dst, this->buffer + this->buffer_position, cur);
|
std::memcpy(dst, this->buffer + this->buffer_position, cur);
|
||||||
|
@ -119,12 +135,7 @@ namespace ams::erpt::srv {
|
||||||
dst_size -= cur;
|
dst_size -= cur;
|
||||||
read_count += cur;
|
read_count += cur;
|
||||||
} else {
|
} else {
|
||||||
if (!opened) {
|
R_TRY(fs::ReadFile(std::addressof(fs_read_size), this->file_handle, this->file_position, this->buffer, this->buffer_size));
|
||||||
R_TRY(fs::OpenFile(std::addressof(tmp_file), this->file_name, fs::OpenMode_Read));
|
|
||||||
opened = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
R_TRY(fs::ReadFile(std::addressof(fs_read_size), tmp_file, this->file_position, this->buffer, this->buffer_size));
|
|
||||||
|
|
||||||
this->buffer_position = 0;
|
this->buffer_position = 0;
|
||||||
this->file_position += static_cast<u32>(fs_read_size);
|
this->file_position += static_cast<u32>(fs_read_size);
|
||||||
|
@ -135,6 +146,12 @@ namespace ams::erpt::srv {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
R_TRY(fs::ReadFile(std::addressof(fs_read_size), this->file_handle, this->file_position, dst, dst_size));
|
||||||
|
|
||||||
|
this->file_position += static_cast<u32>(fs_read_size);
|
||||||
|
read_count = static_cast<u32>(fs_read_size);
|
||||||
|
}
|
||||||
|
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
@ -145,6 +162,7 @@ namespace ams::erpt::srv {
|
||||||
R_UNLESS(this->stream_mode == StreamMode_Write, erpt::ResultNotInitialized());
|
R_UNLESS(this->stream_mode == StreamMode_Write, erpt::ResultNotInitialized());
|
||||||
R_UNLESS(src != nullptr || src_size == 0, erpt::ResultInvalidArgument());
|
R_UNLESS(src != nullptr || src_size == 0, erpt::ResultInvalidArgument());
|
||||||
|
|
||||||
|
if (this->buffer != nullptr) {
|
||||||
while (src_size > 0) {
|
while (src_size > 0) {
|
||||||
if (u32 cur = std::min<u32>(this->buffer_size - this->buffer_count, src_size); cur > 0) {
|
if (u32 cur = std::min<u32>(this->buffer_size - this->buffer_count, src_size); cur > 0) {
|
||||||
std::memcpy(this->buffer + this->buffer_count, src, cur);
|
std::memcpy(this->buffer + this->buffer_count, src, cur);
|
||||||
|
@ -157,19 +175,33 @@ namespace ams::erpt::srv {
|
||||||
R_TRY(this->Flush());
|
R_TRY(this->Flush());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
R_TRY(fs::WriteFile(this->file_handle, this->file_position, src, src_size, fs::WriteOption::None));
|
||||||
|
this->file_position += src_size;
|
||||||
|
}
|
||||||
|
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stream::CloseStream() {
|
void Stream::CloseStream() {
|
||||||
if (this->initialized) {
|
if (this->initialized) {
|
||||||
if (s_can_access_fs && this->stream_mode == StreamMode_Write) {
|
if (s_can_access_fs) {
|
||||||
|
if (this->stream_mode == StreamMode_Write) {
|
||||||
this->Flush();
|
this->Flush();
|
||||||
fs::FlushFile(this->file_handle);
|
fs::FlushFile(this->file_handle);
|
||||||
|
}
|
||||||
fs::CloseFile(this->file_handle);
|
fs::CloseFile(this->file_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this->buffer != nullptr) {
|
||||||
Deallocate(this->buffer);
|
Deallocate(this->buffer);
|
||||||
|
}
|
||||||
|
|
||||||
this->initialized = false;
|
this->initialized = false;
|
||||||
|
|
||||||
|
if (s_fs_commit_mutex.IsLockedByCurrentThread()) {
|
||||||
|
s_fs_commit_mutex.Unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,6 +210,8 @@ namespace ams::erpt::srv {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Stream::Flush() {
|
Result Stream::Flush() {
|
||||||
|
AMS_ASSERT(s_fs_commit_mutex.IsLockedByCurrentThread());
|
||||||
|
|
||||||
R_SUCCEED_IF(this->buffer_count == 0);
|
R_SUCCEED_IF(this->buffer_count == 0);
|
||||||
R_TRY(fs::WriteFile(this->file_handle, this->file_position, this->buffer, this->buffer_count, fs::WriteOption::None));
|
R_TRY(fs::WriteFile(this->file_handle, this->file_position, this->buffer, this->buffer_count, fs::WriteOption::None));
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ namespace ams::erpt::srv {
|
||||||
class Stream {
|
class Stream {
|
||||||
private:
|
private:
|
||||||
static bool s_can_access_fs;
|
static bool s_can_access_fs;
|
||||||
|
static os::SdkMutex s_fs_commit_mutex;
|
||||||
private:
|
private:
|
||||||
u32 buffer_size;
|
u32 buffer_size;
|
||||||
u32 file_position;
|
u32 file_position;
|
||||||
|
|
57
libraries/libstratosphere/source/err/err_api.cpp
Normal file
57
libraries/libstratosphere/source/err/err_api.cpp
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 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 "impl/err_string_util.hpp"
|
||||||
|
|
||||||
|
namespace ams::err {
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr int ErrorCodeCategoryPlatformPrefixForResultModule = 2000;
|
||||||
|
|
||||||
|
ALWAYS_INLINE ErrorCode ConvertResultToErrorCode(const Result &result) {
|
||||||
|
return {
|
||||||
|
.category = static_cast<ErrorCodeCategory>(ErrorCodeCategoryPlatformPrefixForResultModule + result.GetModule()),
|
||||||
|
.number = static_cast<ErrorCodeNumber>(result.GetDescription()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE Result ConvertErrorCodeToResult(const ErrorCode &error_code) {
|
||||||
|
const auto result_value = ::ams::result::impl::ResultTraits::MakeValue(error_code.category - ErrorCodeCategoryPlatformPrefixForResultModule, error_code.number);
|
||||||
|
return ::ams::result::impl::MakeResult(result_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode ConvertResultToErrorCode(const Result &result) {
|
||||||
|
AMS_ASSERT(R_FAILED(result));
|
||||||
|
|
||||||
|
return ::ams::err::impl::ConvertResultToErrorCode(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetErrorCodeString(char *dst, size_t dst_size, ErrorCode error_code) {
|
||||||
|
AMS_ASSERT(dst != nullptr);
|
||||||
|
AMS_ASSERT(dst_size >= static_cast<size_t>(ErrorCode::StringLengthMax));
|
||||||
|
AMS_ASSERT(error_code.IsValid());
|
||||||
|
|
||||||
|
return ::ams::err::impl::MakeErrorCodeString(dst, dst_size, error_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 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 "err_string_util.hpp"
|
||||||
|
|
||||||
|
namespace ams::err::impl {
|
||||||
|
|
||||||
|
void MakeErrorCodeString(char *dst, size_t dst_size, ErrorCode error_code) {
|
||||||
|
const auto len = util::TSNPrintf(dst, dst_size, "%04d-%04d", error_code.category, error_code.number);
|
||||||
|
AMS_ASSERT(static_cast<size_t>(len) < dst_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 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::err::impl {
|
||||||
|
|
||||||
|
void MakeErrorCodeString(char *dst, size_t dst_size, ErrorCode error_code);
|
||||||
|
|
||||||
|
}
|
|
@ -21,7 +21,9 @@ namespace ams::err {
|
||||||
|
|
||||||
R_DEFINE_NAMESPACE_RESULT_MODULE(162);
|
R_DEFINE_NAMESPACE_RESULT_MODULE(162);
|
||||||
|
|
||||||
R_DEFINE_ERROR_RESULT(ApplicationAborted, 1);
|
R_DEFINE_ERROR_RESULT(ApplicationAbort, 1);
|
||||||
R_DEFINE_ERROR_RESULT(SystemModuleAborted, 2);
|
R_DEFINE_ERROR_RESULT(SystemProgramAbort, 2);
|
||||||
|
|
||||||
|
R_DEFINE_ERROR_RESULT(ForcedShutdownDetected, 4);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"title_id": "0x010000000000002b",
|
"title_id": "0x010000000000002b",
|
||||||
"title_id_range_min": "0x010000000000002b",
|
"title_id_range_min": "0x010000000000002b",
|
||||||
"title_id_range_max": "0x010000000000002b",
|
"title_id_range_max": "0x010000000000002b",
|
||||||
"main_thread_stack_size": "0x00001000",
|
"main_thread_stack_size": "0x00002000",
|
||||||
"main_thread_priority": 49,
|
"main_thread_priority": 49,
|
||||||
"default_cpu_id": 3,
|
"default_cpu_id": 3,
|
||||||
"process_category": 0,
|
"process_category": 0,
|
||||||
|
|
|
@ -105,9 +105,9 @@ namespace ams::fatal::srv {
|
||||||
/* Decide whether to generate a report. */
|
/* Decide whether to generate a report. */
|
||||||
this->context.generate_error_report = (policy == FatalPolicy_ErrorReportAndErrorScreen);
|
this->context.generate_error_report = (policy == FatalPolicy_ErrorReportAndErrorScreen);
|
||||||
|
|
||||||
/* Adjust error code (2000-0000 -> 2162-0002). */
|
/* Adjust error code (ResultSuccess()/2000-0000 -> err::ResultSystemProgramAbort()/2162-0002). */
|
||||||
if (R_SUCCEEDED(this->context.result)) {
|
if (R_SUCCEEDED(this->context.result)) {
|
||||||
this->context.result = err::ResultSystemModuleAborted();
|
this->context.result = err::ResultSystemProgramAbort();
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (policy) {
|
switch (policy) {
|
||||||
|
|
Loading…
Reference in a new issue