Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"
This reverts commit 15b7df8ef1.
This commit is contained in:
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
extern lmem::HeapHandle g_heap_handle;
|
||||
|
||||
class Allocator {
|
||||
public:
|
||||
void *operator new(size_t sz) noexcept { return lmem::AllocateFromExpHeap(g_heap_handle, sz); }
|
||||
void *operator new(size_t sz, size_t algn) noexcept { return lmem::AllocateFromExpHeap(g_heap_handle, sz, static_cast<s32>(algn)); }
|
||||
void *operator new[](size_t sz) noexcept { return lmem::AllocateFromExpHeap(g_heap_handle, sz); }
|
||||
void *operator new[](size_t sz, size_t algn) noexcept { return lmem::AllocateFromExpHeap(g_heap_handle, sz, static_cast<s32>(algn)); }
|
||||
|
||||
void operator delete(void *p) noexcept { lmem::FreeToExpHeap(g_heap_handle, p); }
|
||||
void operator delete[](void *p) noexcept { lmem::FreeToExpHeap(g_heap_handle, p); }
|
||||
};
|
||||
|
||||
inline void *Allocate(size_t sz) {
|
||||
return lmem::AllocateFromExpHeap(g_heap_handle, sz);
|
||||
}
|
||||
|
||||
inline void *AllocateWithAlign(size_t sz, size_t align) {
|
||||
return lmem::AllocateFromExpHeap(g_heap_handle, sz, align);
|
||||
}
|
||||
|
||||
inline void Deallocate(void *p) {
|
||||
return lmem::FreeToExpHeap(g_heap_handle, p);
|
||||
}
|
||||
|
||||
inline void DeallocateWithSize(void *p, size_t size) {
|
||||
AMS_UNUSED(size);
|
||||
|
||||
return lmem::FreeToExpHeap(g_heap_handle, p);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* 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_attachment_impl.hpp"
|
||||
#include "erpt_srv_attachment.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
AttachmentFileName Attachment::FileName(AttachmentId attachment_id) {
|
||||
char uuid_str[AttachmentFileNameLength];
|
||||
attachment_id.uuid.ToString(uuid_str, sizeof(uuid_str));
|
||||
|
||||
AttachmentFileName attachment_name;
|
||||
util::SNPrintf(attachment_name.name, sizeof(attachment_name.name), "%s:/%s.att", ReportStoragePath, uuid_str);
|
||||
return attachment_name;
|
||||
}
|
||||
|
||||
Attachment::Attachment(JournalRecord<AttachmentInfo> *r) : m_record(r) {
|
||||
m_record->AddReference();
|
||||
}
|
||||
|
||||
Attachment::~Attachment() {
|
||||
this->CloseStream();
|
||||
if (m_record->RemoveReference()) {
|
||||
this->DeleteStream(this->FileName().name);
|
||||
delete m_record;
|
||||
}
|
||||
}
|
||||
|
||||
AttachmentFileName Attachment::FileName() const {
|
||||
return FileName(m_record->m_info.attachment_id);
|
||||
}
|
||||
|
||||
Result Attachment::Open(AttachmentOpenType type) {
|
||||
switch (type) {
|
||||
case AttachmentOpenType_Create: R_RETURN(this->OpenStream(this->FileName().name, StreamMode_Write, AttachmentStreamBufferSize));
|
||||
case AttachmentOpenType_Read: R_RETURN(this->OpenStream(this->FileName().name, StreamMode_Read, AttachmentStreamBufferSize));
|
||||
default: R_THROW(erpt::ResultInvalidArgument());
|
||||
}
|
||||
}
|
||||
|
||||
Result Attachment::Read(u32 *out_read_count, u8 *dst, u32 dst_size) {
|
||||
R_RETURN(this->ReadStream(out_read_count, dst, dst_size));
|
||||
}
|
||||
|
||||
Result Attachment::Delete() {
|
||||
R_RETURN(this->DeleteStream(this->FileName().name));
|
||||
}
|
||||
|
||||
void Attachment::Close() {
|
||||
return this->CloseStream();
|
||||
}
|
||||
|
||||
Result Attachment::GetFlags(AttachmentFlagSet *out) const {
|
||||
*out = m_record->m_info.flags;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Attachment::SetFlags(AttachmentFlagSet flags) {
|
||||
if (((~m_record->m_info.flags) & flags).IsAnySet()) {
|
||||
m_record->m_info.flags |= flags;
|
||||
R_RETURN(Journal::Commit());
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Attachment::GetSize(s64 *out) const {
|
||||
R_RETURN(this->GetStreamSize(out));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* 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>
|
||||
#include "erpt_srv_allocator.hpp"
|
||||
#include "erpt_srv_stream.hpp"
|
||||
#include "erpt_srv_journal.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
enum AttachmentOpenType {
|
||||
AttachmentOpenType_Create = 0,
|
||||
AttachmentOpenType_Read = 1,
|
||||
};
|
||||
|
||||
constexpr inline u32 AttachmentStreamBufferSize = 1_KB;
|
||||
|
||||
class Attachment : public Allocator, public Stream {
|
||||
private:
|
||||
JournalRecord<AttachmentInfo> *m_record;
|
||||
private:
|
||||
AttachmentFileName FileName() const;
|
||||
public:
|
||||
static AttachmentFileName FileName(AttachmentId attachment_id);
|
||||
public:
|
||||
explicit Attachment(JournalRecord<AttachmentInfo> *r);
|
||||
~Attachment();
|
||||
|
||||
Result Open(AttachmentOpenType type);
|
||||
Result Read(u32 *out_read_count, u8 *dst, u32 dst_size);
|
||||
Result Delete();
|
||||
void Close();
|
||||
|
||||
Result GetFlags(AttachmentFlagSet *out) const;
|
||||
Result SetFlags(AttachmentFlagSet flags);
|
||||
Result GetSize(s64 *out) const;
|
||||
|
||||
template<typename T>
|
||||
Result Write(T val) {
|
||||
R_RETURN(this->WriteStream(reinterpret_cast<const u8 *>(std::addressof(val)), sizeof(val)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Result Write(const T *buf, u32 buffer_size) {
|
||||
R_RETURN(this->WriteStream(reinterpret_cast<const u8 *>(buf), buffer_size));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* 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_attachment_impl.hpp"
|
||||
#include "erpt_srv_attachment.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
AttachmentImpl::AttachmentImpl() : m_attachment(nullptr) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
AttachmentImpl::~AttachmentImpl() {
|
||||
R_ABORT_UNLESS(this->Close());
|
||||
}
|
||||
|
||||
Result AttachmentImpl::Open(const AttachmentId &attachment_id) {
|
||||
R_UNLESS(m_attachment == nullptr, erpt::ResultAlreadyInitialized());
|
||||
|
||||
JournalRecord<AttachmentInfo> *record = Journal::Retrieve(attachment_id);
|
||||
R_UNLESS(record != nullptr, erpt::ResultNotFound());
|
||||
|
||||
m_attachment = new Attachment(record);
|
||||
R_UNLESS(m_attachment != nullptr, erpt::ResultOutOfMemory());
|
||||
auto attachment_guard = SCOPE_GUARD { delete m_attachment; m_attachment = nullptr; };
|
||||
|
||||
R_TRY(m_attachment->Open(AttachmentOpenType_Read));
|
||||
attachment_guard.Cancel();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result AttachmentImpl::Read(ams::sf::Out<u32> out_count, const ams::sf::OutBuffer &out_buffer) {
|
||||
R_UNLESS(m_attachment != nullptr, erpt::ResultNotInitialized());
|
||||
|
||||
R_RETURN(m_attachment->Read(out_count.GetPointer(), static_cast<u8 *>(out_buffer.GetPointer()), static_cast<u32>(out_buffer.GetSize())));
|
||||
}
|
||||
|
||||
Result AttachmentImpl::SetFlags(AttachmentFlagSet flags) {
|
||||
R_UNLESS(m_attachment != nullptr, erpt::ResultNotInitialized());
|
||||
|
||||
R_RETURN(m_attachment->SetFlags(flags));
|
||||
}
|
||||
|
||||
Result AttachmentImpl::GetFlags(ams::sf::Out<AttachmentFlagSet> out) {
|
||||
R_UNLESS(m_attachment != nullptr, erpt::ResultNotInitialized());
|
||||
|
||||
R_RETURN(m_attachment->GetFlags(out.GetPointer()));
|
||||
}
|
||||
|
||||
Result AttachmentImpl::Close() {
|
||||
if (m_attachment != nullptr) {
|
||||
m_attachment->Close();
|
||||
delete m_attachment;
|
||||
m_attachment = nullptr;
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result AttachmentImpl::GetSize(ams::sf::Out<s64> out) {
|
||||
R_UNLESS(m_attachment != nullptr, erpt::ResultNotInitialized());
|
||||
|
||||
R_RETURN(m_attachment->GetSize(out.GetPointer()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* 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 Attachment;
|
||||
|
||||
class AttachmentImpl {
|
||||
private:
|
||||
Attachment *m_attachment;
|
||||
public:
|
||||
AttachmentImpl();
|
||||
~AttachmentImpl();
|
||||
public:
|
||||
Result Open(const AttachmentId &attachment_id);
|
||||
Result Read(ams::sf::Out<u32> out_count, const ams::sf::OutBuffer &out_buffer);
|
||||
Result SetFlags(AttachmentFlagSet flags);
|
||||
Result GetFlags(ams::sf::Out<AttachmentFlagSet> out);
|
||||
Result Close();
|
||||
Result GetSize(ams::sf::Out<s64> out);
|
||||
};
|
||||
static_assert(erpt::sf::IsIAttachment<AttachmentImpl>);
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* 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_cipher.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
constinit u8 Cipher::s_key[crypto::Aes128CtrEncryptor::KeySize + crypto::Aes128CtrEncryptor::IvSize + crypto::Aes128CtrEncryptor::BlockSize];
|
||||
constinit bool Cipher::s_need_to_store_cipher = false;
|
||||
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* 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>
|
||||
#include "erpt_srv_formatter.hpp"
|
||||
#include "erpt_srv_keys.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
class Cipher : private Formatter {
|
||||
private:
|
||||
static constexpr u32 RsaKeySize = 0x100;
|
||||
static constexpr u32 SaltSize = 0x20;
|
||||
|
||||
static u8 s_key[crypto::Aes128CtrEncryptor::KeySize + crypto::Aes128CtrEncryptor::IvSize + crypto::Aes128CtrEncryptor::BlockSize];
|
||||
static bool s_need_to_store_cipher;
|
||||
|
||||
struct Header {
|
||||
u32 magic;
|
||||
u32 field_type;
|
||||
u32 element_count;
|
||||
u32 reserved;
|
||||
u8 data[0];
|
||||
};
|
||||
static_assert(sizeof(Header) == 0x10);
|
||||
|
||||
static constexpr u32 HeaderMagic = util::FourCC<'C', 'R', 'P', 'T'>::Code;
|
||||
private:
|
||||
template<typename T>
|
||||
static Result EncryptArray(Report *report, FieldId field_id, T *arr, u32 arr_size) {
|
||||
const u32 data_size = util::AlignUp(arr_size * sizeof(T), crypto::Aes128CtrEncryptor::BlockSize);
|
||||
|
||||
Header *hdr = reinterpret_cast<Header *>(AllocateWithAlign(sizeof(Header) + data_size, crypto::Aes128CtrEncryptor::BlockSize));
|
||||
R_UNLESS(hdr != nullptr, erpt::ResultOutOfMemory());
|
||||
ON_SCOPE_EXIT { Deallocate(hdr); };
|
||||
|
||||
hdr->magic = HeaderMagic;
|
||||
hdr->field_type = static_cast<u32>(ConvertFieldToType(field_id));
|
||||
hdr->element_count = arr_size;
|
||||
hdr->reserved = 0;
|
||||
|
||||
std::memset(hdr->data, 0, data_size);
|
||||
std::memcpy(hdr->data, arr, arr_size * sizeof(T));
|
||||
|
||||
crypto::EncryptAes128Ctr(hdr->data, data_size, s_key, crypto::Aes128CtrEncryptor::KeySize, s_key + crypto::Aes128CtrEncryptor::KeySize, crypto::Aes128CtrEncryptor::IvSize, hdr->data, data_size);
|
||||
|
||||
ON_SCOPE_EXIT { std::memset(hdr, 0, sizeof(hdr) + data_size); s_need_to_store_cipher = true; };
|
||||
|
||||
R_RETURN(Formatter::AddField(report, field_id, reinterpret_cast<u8 *>(hdr), sizeof(hdr) + data_size));
|
||||
}
|
||||
public:
|
||||
static Result Begin(Report *report, u32 record_count) {
|
||||
s_need_to_store_cipher = false;
|
||||
crypto::GenerateCryptographicallyRandomBytes(s_key, sizeof(s_key));
|
||||
|
||||
R_RETURN(Formatter::Begin(report, record_count + 1));
|
||||
}
|
||||
|
||||
static Result End(Report *report) {
|
||||
u8 cipher[RsaKeySize] = {};
|
||||
|
||||
if (s_need_to_store_cipher) {
|
||||
u8 salt[SaltSize];
|
||||
crypto::RsaOaepEncryptor<RsaKeySize, crypto::Sha256Generator> oaep;
|
||||
crypto::GenerateCryptographicallyRandomBytes(salt, sizeof(salt));
|
||||
|
||||
oaep.Initialize(GetPublicKeyModulus(), GetPublicKeyModulusSize(), GetPublicKeyExponent(), GetPublicKeyExponentSize());
|
||||
oaep.Encrypt(cipher, sizeof(cipher), s_key, sizeof(s_key), salt, sizeof(salt));
|
||||
}
|
||||
|
||||
Formatter::AddField(report, FieldId_CipherKey, cipher, sizeof(cipher));
|
||||
std::memset(s_key, 0, sizeof(s_key));
|
||||
|
||||
R_RETURN(Formatter::End(report));
|
||||
}
|
||||
|
||||
static Result AddField(Report *report, FieldId field_id, bool value) {
|
||||
R_RETURN(Formatter::AddField(report, field_id, value));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static Result AddField(Report *report, FieldId field_id, T value) {
|
||||
R_RETURN(Formatter::AddField<T>(report, field_id, value));
|
||||
}
|
||||
|
||||
static Result AddField(Report *report, FieldId field_id, char *str, u32 len) {
|
||||
if (ConvertFieldToFlag(field_id) == FieldFlag_Encrypt) {
|
||||
R_RETURN(EncryptArray<char>(report, field_id, str, len));
|
||||
} else {
|
||||
R_RETURN(Formatter::AddField(report, field_id, str, len));
|
||||
}
|
||||
}
|
||||
|
||||
static Result AddField(Report *report, FieldId field_id, u8 *bin, u32 len) {
|
||||
if (ConvertFieldToFlag(field_id) == FieldFlag_Encrypt) {
|
||||
R_RETURN(EncryptArray<u8>(report, field_id, bin, len));
|
||||
} else {
|
||||
R_RETURN(Formatter::AddField(report, field_id, bin, len));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static Result AddField(Report *report, FieldId field_id, T *arr, u32 len) {
|
||||
if (ConvertFieldToFlag(field_id) == FieldFlag_Encrypt) {
|
||||
R_RETURN(EncryptArray<T>(report, field_id, arr, len));
|
||||
} else {
|
||||
R_RETURN(Formatter::AddField<T>(report, field_id, arr, len));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* 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_context.hpp"
|
||||
#include "erpt_srv_cipher.hpp"
|
||||
#include "erpt_srv_context_record.hpp"
|
||||
#include "erpt_srv_report.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
namespace {
|
||||
|
||||
using ContextList = util::IntrusiveListBaseTraits<Context>::ListType;
|
||||
|
||||
constinit ContextList g_category_list;
|
||||
|
||||
}
|
||||
|
||||
Context::Context(CategoryId cat) : m_category(cat) {
|
||||
g_category_list.push_front(*this);
|
||||
}
|
||||
|
||||
Context::~Context() {
|
||||
g_category_list.erase(g_category_list.iterator_to(*this));
|
||||
}
|
||||
|
||||
Result Context::AddCategoryToReport(Report *report) {
|
||||
if (m_record != nullptr) {
|
||||
const auto *entry = m_record->GetContextEntryPtr();
|
||||
for (u32 i = 0; i < entry->field_count; i++) {
|
||||
auto *field = std::addressof(entry->fields[i]);
|
||||
u8 *arr_buf = entry->array_buffer;
|
||||
|
||||
switch (field->type) {
|
||||
case FieldType_Bool: R_TRY(Cipher::AddField(report, field->id, field->value_bool)); break;
|
||||
case FieldType_NumericU8: R_TRY(Cipher::AddField(report, field->id, field->value_u8)); break;
|
||||
case FieldType_NumericU16: R_TRY(Cipher::AddField(report, field->id, field->value_u16)); break;
|
||||
case FieldType_NumericU32: R_TRY(Cipher::AddField(report, field->id, field->value_u32)); break;
|
||||
case FieldType_NumericU64: R_TRY(Cipher::AddField(report, field->id, field->value_u64)); break;
|
||||
case FieldType_NumericI8: R_TRY(Cipher::AddField(report, field->id, field->value_i8)); break;
|
||||
case FieldType_NumericI16: R_TRY(Cipher::AddField(report, field->id, field->value_i16)); break;
|
||||
case FieldType_NumericI32: R_TRY(Cipher::AddField(report, field->id, field->value_i32)); break;
|
||||
case FieldType_NumericI64: R_TRY(Cipher::AddField(report, field->id, field->value_i64)); break;
|
||||
case FieldType_String: R_TRY(Cipher::AddField(report, field->id, reinterpret_cast<char *>(arr_buf + field->value_array.start_idx), field->value_array.size / sizeof(char))); break;
|
||||
case FieldType_U8Array: R_TRY(Cipher::AddField(report, field->id, reinterpret_cast< u8 *>(arr_buf + field->value_array.start_idx), field->value_array.size / sizeof(u8))); break;
|
||||
case FieldType_U32Array: R_TRY(Cipher::AddField(report, field->id, reinterpret_cast< u32 *>(arr_buf + field->value_array.start_idx), field->value_array.size / sizeof(u32))); break;
|
||||
case FieldType_U64Array: R_TRY(Cipher::AddField(report, field->id, reinterpret_cast< u64 *>(arr_buf + field->value_array.start_idx), field->value_array.size / sizeof(u64))); break;
|
||||
case FieldType_I8Array: R_TRY(Cipher::AddField(report, field->id, reinterpret_cast< s8 *>(arr_buf + field->value_array.start_idx), field->value_array.size / sizeof(s8))); break;
|
||||
case FieldType_I32Array: R_TRY(Cipher::AddField(report, field->id, reinterpret_cast< s32 *>(arr_buf + field->value_array.start_idx), field->value_array.size / sizeof(s32))); break;
|
||||
case FieldType_I64Array: R_TRY(Cipher::AddField(report, field->id, reinterpret_cast< s64 *>(arr_buf + field->value_array.start_idx), field->value_array.size / sizeof(s64))); break;
|
||||
default: R_THROW(erpt::ResultInvalidArgument());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Context::SubmitContext(const ContextEntry *entry, const u8 *data, u32 data_size) {
|
||||
auto record = std::make_unique<ContextRecord>();
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
R_TRY(record->Initialize(entry, data, data_size));
|
||||
|
||||
R_RETURN(SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
|
||||
Result Context::SubmitContextRecord(std::unique_ptr<ContextRecord> record) {
|
||||
auto it = util::range::find_if(g_category_list, [&](const Context &cur) {
|
||||
return cur.m_category == record->GetContextEntryPtr()->category;
|
||||
});
|
||||
R_UNLESS(it != g_category_list.end(), erpt::ResultCategoryNotFound());
|
||||
|
||||
it->m_record = std::move(record);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Context::WriteContextsToReport(Report *report) {
|
||||
R_TRY(report->Open(ReportOpenType_Create));
|
||||
R_TRY(Cipher::Begin(report, ContextRecord::GetRecordCount()));
|
||||
|
||||
for (auto it = g_category_list.begin(); it != g_category_list.end(); it++) {
|
||||
R_TRY(it->AddCategoryToReport(report));
|
||||
}
|
||||
|
||||
Cipher::End(report);
|
||||
report->Close();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Context::ClearContext(CategoryId cat) {
|
||||
/* Make an empty record for the category. */
|
||||
auto record = std::make_unique<ContextRecord>(cat);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Submit the context record. */
|
||||
R_RETURN(SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* 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>
|
||||
#include "erpt_srv_allocator.hpp"
|
||||
#include "erpt_srv_cipher.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
class ContextRecord;
|
||||
class Report;
|
||||
|
||||
class Context : public Allocator, public util::IntrusiveListBaseNode<Context> {
|
||||
private:
|
||||
const CategoryId m_category;
|
||||
std::unique_ptr<ContextRecord> m_record;
|
||||
public:
|
||||
Context(CategoryId cat);
|
||||
~Context();
|
||||
|
||||
Result AddCategoryToReport(Report *report);
|
||||
public:
|
||||
static Result SubmitContext(const ContextEntry *entry, const u8 *data, u32 data_size);
|
||||
static Result SubmitContextRecord(std::unique_ptr<ContextRecord> record);
|
||||
static Result WriteContextsToReport(Report *report);
|
||||
static Result ClearContext(CategoryId cat);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
/*
|
||||
* 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_context_impl.hpp"
|
||||
#include "erpt_srv_manager_impl.hpp"
|
||||
#include "erpt_srv_context.hpp"
|
||||
#include "erpt_srv_reporter.hpp"
|
||||
#include "erpt_srv_journal.hpp"
|
||||
#include "erpt_srv_forced_shutdown.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
Result ContextImpl::SubmitContext(const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer) {
|
||||
const ContextEntry *ctx = reinterpret_cast<const ContextEntry *>( ctx_buffer.GetPointer());
|
||||
const u8 *data = reinterpret_cast<const u8 *>(data_buffer.GetPointer());
|
||||
|
||||
const u32 ctx_size = static_cast<u32>(ctx_buffer.GetSize());
|
||||
const u32 data_size = static_cast<u32>(data_buffer.GetSize());
|
||||
|
||||
R_UNLESS(ctx_size == sizeof(ContextEntry), erpt::ResultInvalidArgument());
|
||||
R_UNLESS(data_size <= ArrayBufferSizeMax, erpt::ResultInvalidArgument());
|
||||
|
||||
SubmitContextForForcedShutdownDetection(ctx, data, data_size);
|
||||
|
||||
R_RETURN(Context::SubmitContext(ctx, data, data_size));
|
||||
}
|
||||
|
||||
Result ContextImpl::CreateReport(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &meta_buffer, Result result, erpt::CreateReportOptionFlagSet flags) {
|
||||
const ContextEntry *ctx = reinterpret_cast<const ContextEntry *>( ctx_buffer.GetPointer());
|
||||
const u8 *data = reinterpret_cast<const u8 *>(data_buffer.GetPointer());
|
||||
const ReportMetaData *meta = reinterpret_cast<const ReportMetaData *>(meta_buffer.GetPointer());
|
||||
|
||||
const u32 ctx_size = static_cast<u32>(ctx_buffer.GetSize());
|
||||
const u32 data_size = static_cast<u32>(data_buffer.GetSize());
|
||||
const u32 meta_size = static_cast<u32>(meta_buffer.GetSize());
|
||||
|
||||
R_UNLESS(ctx_size == sizeof(ContextEntry), erpt::ResultInvalidArgument());
|
||||
R_UNLESS(meta_size == 0 || meta_size == sizeof(ReportMetaData), erpt::ResultInvalidArgument());
|
||||
|
||||
R_TRY(Reporter::CreateReport(report_type, result, ctx, data, data_size, meta_size != 0 ? meta : nullptr, nullptr, 0, flags, nullptr));
|
||||
|
||||
ManagerImpl::NotifyAll();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ContextImpl::CreateReportV1(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &meta_buffer, Result result) {
|
||||
R_RETURN(this->CreateReport(report_type, ctx_buffer, data_buffer, meta_buffer, result, erpt::srv::MakeNoCreateReportOptionFlags()));
|
||||
}
|
||||
|
||||
Result ContextImpl::CreateReportV0(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &meta_buffer) {
|
||||
R_RETURN(this->CreateReportV1(report_type, ctx_buffer, data_buffer, meta_buffer, ResultSuccess()));
|
||||
}
|
||||
|
||||
Result ContextImpl::SetInitialLaunchSettingsCompletionTime(const time::SteadyClockTimePoint &time_point) {
|
||||
Reporter::SetInitialLaunchSettingsCompletionTime(time_point);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ContextImpl::ClearInitialLaunchSettingsCompletionTime() {
|
||||
Reporter::ClearInitialLaunchSettingsCompletionTime();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ContextImpl::UpdatePowerOnTime() {
|
||||
/* NOTE: Prior to 12.0.0, this set the power on time, but now erpt does it during initialization. */
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ContextImpl::UpdateAwakeTime() {
|
||||
/* NOTE: Prior to 12.0.0, this set the power on time, but now erpt does it during initialization. */
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ContextImpl::SubmitMultipleCategoryContext(const MultipleCategoryContextEntry &ctx_entry, const ams::sf::InBuffer &str_buffer) {
|
||||
R_UNLESS(ctx_entry.category_count <= CategoriesPerMultipleCategoryContext, erpt::ResultInvalidArgument());
|
||||
|
||||
const u8 *str = reinterpret_cast<const u8 *>(str_buffer.GetPointer());
|
||||
const u32 str_size = static_cast<u32>(str_buffer.GetSize());
|
||||
|
||||
u32 total_field_count = 0, total_arr_count = 0;
|
||||
for (u32 i = 0; i < ctx_entry.category_count; i++) {
|
||||
ContextEntry entry = {
|
||||
.version = ctx_entry.version,
|
||||
.field_count = ctx_entry.field_counts[i],
|
||||
.category = ctx_entry.categories[i],
|
||||
};
|
||||
R_UNLESS(entry.field_count <= erpt::FieldsPerContext, erpt::ResultInvalidArgument());
|
||||
R_UNLESS(entry.field_count + total_field_count <= erpt::FieldsPerMultipleCategoryContext, erpt::ResultInvalidArgument());
|
||||
R_UNLESS(ctx_entry.array_buf_counts[i] <= ArrayBufferSizeMax, erpt::ResultInvalidArgument());
|
||||
R_UNLESS(ctx_entry.array_buf_counts[i] + total_arr_count <= str_size, erpt::ResultInvalidArgument());
|
||||
|
||||
std::memcpy(entry.fields, ctx_entry.fields + total_field_count, entry.field_count * sizeof(FieldEntry));
|
||||
|
||||
R_TRY(Context::SubmitContext(std::addressof(entry), str + total_arr_count, ctx_entry.array_buf_counts[i]));
|
||||
|
||||
total_field_count += entry.field_count;
|
||||
total_arr_count += ctx_entry.array_buf_counts[i];
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ContextImpl::UpdateApplicationLaunchTime() {
|
||||
Reporter::UpdateApplicationLaunchTime();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ContextImpl::ClearApplicationLaunchTime() {
|
||||
Reporter::ClearApplicationLaunchTime();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ContextImpl::SubmitAttachment(ams::sf::Out<AttachmentId> out, const ams::sf::InBuffer &attachment_name, const ams::sf::InBuffer &attachment_data) {
|
||||
const char *name = reinterpret_cast<const char *>(attachment_name.GetPointer());
|
||||
const u8 *data = reinterpret_cast<const u8 *>(attachment_data.GetPointer());
|
||||
|
||||
const u32 name_size = static_cast<u32>(attachment_name.GetSize());
|
||||
const u32 data_size = static_cast<u32>(attachment_data.GetSize());
|
||||
|
||||
R_UNLESS(data != nullptr, erpt::ResultInvalidArgument());
|
||||
R_UNLESS(data_size <= AttachmentSizeMax, erpt::ResultInvalidArgument());
|
||||
R_UNLESS(name != nullptr, erpt::ResultInvalidArgument());
|
||||
R_UNLESS(name_size <= AttachmentNameSizeMax, erpt::ResultInvalidArgument());
|
||||
|
||||
char name_safe[AttachmentNameSizeMax];
|
||||
util::Strlcpy(name_safe, name, sizeof(name_safe));
|
||||
|
||||
R_RETURN(JournalForAttachments::SubmitAttachment(out.GetPointer(), name_safe, data, data_size));
|
||||
}
|
||||
|
||||
Result ContextImpl::SubmitAttachmentWithLz4Compression(ams::sf::Out<AttachmentId> out, const ams::sf::InBuffer &attachment_name, const ams::sf::InBuffer &attachment_data) {
|
||||
/* TODO: Implement LZ4 compression on attachments. */
|
||||
R_RETURN(this->SubmitAttachment(out, attachment_name, attachment_data));
|
||||
}
|
||||
|
||||
Result ContextImpl::CreateReportWithAttachments(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &attachment_ids_buffer, Result result, erpt::CreateReportOptionFlagSet flags) {
|
||||
const ContextEntry *ctx = reinterpret_cast<const ContextEntry *>( ctx_buffer.GetPointer());
|
||||
const u8 *data = reinterpret_cast<const u8 *>(data_buffer.GetPointer());
|
||||
const u32 ctx_size = static_cast<u32>(ctx_buffer.GetSize());
|
||||
const u32 data_size = static_cast<u32>(data_buffer.GetSize());
|
||||
|
||||
const AttachmentId *attachments = reinterpret_cast<const AttachmentId *>(attachment_ids_buffer.GetPointer());
|
||||
const u32 num_attachments = attachment_ids_buffer.GetSize() / sizeof(*attachments);
|
||||
|
||||
R_UNLESS(ctx_size == sizeof(ContextEntry), erpt::ResultInvalidArgument());
|
||||
R_UNLESS(num_attachments <= AttachmentsPerReportMax, erpt::ResultInvalidArgument());
|
||||
|
||||
R_TRY(Reporter::CreateReport(report_type, result, ctx, data, data_size, nullptr, attachments, num_attachments, flags, nullptr));
|
||||
|
||||
ManagerImpl::NotifyAll();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ContextImpl::CreateReportWithAttachmentsDeprecated2(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &attachment_ids_buffer, Result result) {
|
||||
R_RETURN(this->CreateReportWithAttachments(report_type, ctx_buffer, data_buffer, attachment_ids_buffer, result, erpt::srv::MakeNoCreateReportOptionFlags()));
|
||||
}
|
||||
|
||||
Result ContextImpl::CreateReportWithAttachmentsDeprecated(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &attachment_ids_buffer) {
|
||||
R_RETURN(this->CreateReportWithAttachmentsDeprecated2(report_type, ctx_buffer, data_buffer, attachment_ids_buffer, ResultSuccess()));
|
||||
}
|
||||
|
||||
Result ContextImpl::CreateReportWithSpecifiedReprotId(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &meta_buffer, const ams::sf::InBuffer &attachment_ids_buffer, Result result, erpt::CreateReportOptionFlagSet flags, const ReportId &report_id) {
|
||||
const ContextEntry *ctx = reinterpret_cast<const ContextEntry *>( ctx_buffer.GetPointer());
|
||||
const u8 *data = reinterpret_cast<const u8 *>(data_buffer.GetPointer());
|
||||
const ReportMetaData *meta = reinterpret_cast<const ReportMetaData *>(meta_buffer.GetPointer());
|
||||
|
||||
const u32 ctx_size = static_cast<u32>(ctx_buffer.GetSize());
|
||||
const u32 data_size = static_cast<u32>(data_buffer.GetSize());
|
||||
const u32 meta_size = static_cast<u32>(meta_buffer.GetSize());
|
||||
|
||||
const AttachmentId *attachments = reinterpret_cast<const AttachmentId *>(attachment_ids_buffer.GetPointer());
|
||||
const u32 num_attachments = attachment_ids_buffer.GetSize() / sizeof(*attachments);
|
||||
|
||||
R_UNLESS(ctx_size == sizeof(ContextEntry), erpt::ResultInvalidArgument());
|
||||
R_UNLESS(meta_size == 0 || meta_size == sizeof(ReportMetaData), erpt::ResultInvalidArgument());
|
||||
R_UNLESS(num_attachments <= AttachmentsPerReportMax, erpt::ResultInvalidArgument());
|
||||
|
||||
R_TRY(Reporter::CreateReport(report_type, result, ctx, data, data_size, meta_size != 0 ? meta : nullptr, attachments, num_attachments, flags, std::addressof(report_id)));
|
||||
|
||||
ManagerImpl::NotifyAll();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ContextImpl::RegisterRunningApplet(ncm::ProgramId program_id) {
|
||||
R_RETURN(Reporter::RegisterRunningApplet(program_id));
|
||||
}
|
||||
|
||||
Result ContextImpl::UnregisterRunningApplet(ncm::ProgramId program_id) {
|
||||
R_RETURN(Reporter::UnregisterRunningApplet(program_id));
|
||||
}
|
||||
|
||||
Result ContextImpl::UpdateAppletSuspendedDuration(ncm::ProgramId program_id, TimeSpanType duration) {
|
||||
R_RETURN(Reporter::UpdateAppletSuspendedDuration(program_id, duration));
|
||||
}
|
||||
|
||||
Result ContextImpl::InvalidateForcedShutdownDetection() {
|
||||
/* NOTE: Nintendo does not check the result here. */
|
||||
erpt::srv::InvalidateForcedShutdownDetection();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* 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 ContextImpl {
|
||||
public:
|
||||
Result SubmitContext(const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer);
|
||||
Result CreateReportV0(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &meta_buffer);
|
||||
Result SetInitialLaunchSettingsCompletionTime(const time::SteadyClockTimePoint &time_point);
|
||||
Result ClearInitialLaunchSettingsCompletionTime();
|
||||
Result UpdatePowerOnTime();
|
||||
Result UpdateAwakeTime();
|
||||
Result SubmitMultipleCategoryContext(const MultipleCategoryContextEntry &ctx_entry, const ams::sf::InBuffer &str_buffer);
|
||||
Result UpdateApplicationLaunchTime();
|
||||
Result ClearApplicationLaunchTime();
|
||||
Result SubmitAttachment(ams::sf::Out<AttachmentId> out, const ams::sf::InBuffer &attachment_name, const ams::sf::InBuffer &attachment_data);
|
||||
Result CreateReportWithAttachmentsDeprecated(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &attachment_ids_buffer);
|
||||
Result CreateReportWithAttachmentsDeprecated2(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &attachment_ids_buffer, Result result);
|
||||
Result CreateReportWithAttachments(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &attachment_ids_buffer, Result result, erpt::CreateReportOptionFlagSet flags);
|
||||
Result CreateReportV1(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &meta_buffer, Result result);
|
||||
Result CreateReport(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &meta_buffer, Result result, erpt::CreateReportOptionFlagSet flags);
|
||||
Result SubmitAttachmentWithLz4Compression(ams::sf::Out<AttachmentId> out, const ams::sf::InBuffer &attachment_name, const ams::sf::InBuffer &attachment_data);
|
||||
Result CreateReportWithSpecifiedReprotId(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &meta_buffer, const ams::sf::InBuffer &attachment_data, Result result, erpt::CreateReportOptionFlagSet flags, const ReportId &report_id);
|
||||
Result RegisterRunningApplet(ncm::ProgramId program_id);
|
||||
Result UnregisterRunningApplet(ncm::ProgramId program_id);
|
||||
Result UpdateAppletSuspendedDuration(ncm::ProgramId program_id, TimeSpanType duration);
|
||||
Result InvalidateForcedShutdownDetection();
|
||||
};
|
||||
static_assert(erpt::sf::IsIContext<ContextImpl>);
|
||||
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
/*
|
||||
* 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_context_record.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
constinit u32 ContextRecord::s_record_count = 0;
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsArrayFieldType(FieldType type) {
|
||||
return type == FieldType_String ||
|
||||
type == FieldType_U8Array ||
|
||||
type == FieldType_U32Array ||
|
||||
type == FieldType_U64Array ||
|
||||
type == FieldType_I32Array ||
|
||||
type == FieldType_I64Array;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ContextRecord::ContextRecord() {
|
||||
m_ctx = {};
|
||||
}
|
||||
|
||||
ContextRecord::ContextRecord(CategoryId category, u32 array_buf_size) {
|
||||
m_ctx = {
|
||||
.category = category,
|
||||
.array_buffer = static_cast<u8 *>(Allocate(array_buf_size)),
|
||||
};
|
||||
if (m_ctx.array_buffer != nullptr) {
|
||||
m_ctx.array_buffer_size = array_buf_size;
|
||||
m_ctx.array_free_count = array_buf_size;
|
||||
}
|
||||
}
|
||||
|
||||
ContextRecord::~ContextRecord() {
|
||||
if (m_ctx.array_buffer != nullptr) {
|
||||
Deallocate(m_ctx.array_buffer);
|
||||
}
|
||||
|
||||
AMS_ABORT_UNLESS(s_record_count >= m_ctx.field_count);
|
||||
s_record_count -= m_ctx.field_count;
|
||||
}
|
||||
|
||||
Result ContextRecord::Initialize(const ContextEntry *ctx_ptr, const u8 *data, u32 data_size) {
|
||||
R_UNLESS(data_size <= ArrayBufferSizeMax, erpt::ResultInvalidArgument());
|
||||
|
||||
m_ctx.version = ctx_ptr->version;
|
||||
m_ctx.field_count = ctx_ptr->field_count;
|
||||
m_ctx.category = ctx_ptr->category;
|
||||
m_ctx.array_buffer = nullptr;
|
||||
m_ctx.array_buffer_size = data_size;
|
||||
m_ctx.array_free_count = 0;
|
||||
|
||||
auto guard = SCOPE_GUARD { m_ctx.field_count = 0; };
|
||||
|
||||
R_UNLESS(m_ctx.field_count <= FieldsPerContext, erpt::ResultInvalidArgument());
|
||||
R_UNLESS(FindCategoryIndex(m_ctx.category).has_value(), erpt::ResultInvalidArgument());
|
||||
|
||||
for (u32 i = 0; i < m_ctx.field_count; i++) {
|
||||
m_ctx.fields[i] = ctx_ptr->fields[i];
|
||||
|
||||
R_UNLESS(FindFieldIndex(m_ctx.fields[i].id).has_value(), erpt::ResultInvalidArgument());
|
||||
R_UNLESS(0 <= m_ctx.fields[i].type && m_ctx.fields[i].type < FieldType_Count, erpt::ResultInvalidArgument());
|
||||
|
||||
R_UNLESS(m_ctx.fields[i].type == ConvertFieldToType(m_ctx.fields[i].id), erpt::ResultFieldTypeMismatch());
|
||||
R_UNLESS(m_ctx.category == ConvertFieldToCategory(m_ctx.fields[i].id), erpt::ResultFieldCategoryMismatch());
|
||||
|
||||
if (IsArrayFieldType(m_ctx.fields[i].type)) {
|
||||
const u32 start_idx = m_ctx.fields[i].value_array.start_idx;
|
||||
const u32 size = m_ctx.fields[i].value_array.size;
|
||||
const u32 end_idx = start_idx + size;
|
||||
|
||||
R_UNLESS(start_idx <= data_size, erpt::ResultInvalidArgument());
|
||||
R_UNLESS(size <= data_size, erpt::ResultInvalidArgument());
|
||||
R_UNLESS(end_idx <= data_size, erpt::ResultInvalidArgument());
|
||||
R_UNLESS(size <= ArrayFieldSizeMax, erpt::ResultInvalidArgument());
|
||||
}
|
||||
}
|
||||
|
||||
if (data_size > 0) {
|
||||
/* If array buffer isn't nullptr, we'll leak memory here, so verify that it is. */
|
||||
AMS_ABORT_UNLESS(m_ctx.array_buffer == nullptr);
|
||||
|
||||
m_ctx.array_buffer = static_cast<u8 *>(AllocateWithAlign(data_size, alignof(u64)));
|
||||
R_UNLESS(m_ctx.array_buffer != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
std::memcpy(m_ctx.array_buffer, data, data_size);
|
||||
}
|
||||
|
||||
guard.Cancel();
|
||||
s_record_count += m_ctx.field_count;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ContextRecord::Add(FieldId field_id, bool value_bool) {
|
||||
R_UNLESS(m_ctx.field_count < FieldsPerContext, erpt::ResultOutOfFieldSpace());
|
||||
|
||||
s_record_count++;
|
||||
auto &field = m_ctx.fields[m_ctx.field_count++];
|
||||
|
||||
field.id = field_id;
|
||||
field.type = FieldType_Bool;
|
||||
|
||||
field.value_bool = value_bool;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ContextRecord::Add(FieldId field_id, u32 value_u32) {
|
||||
R_UNLESS(m_ctx.field_count < FieldsPerContext, erpt::ResultOutOfFieldSpace());
|
||||
|
||||
s_record_count++;
|
||||
auto &field = m_ctx.fields[m_ctx.field_count++];
|
||||
|
||||
field.id = field_id;
|
||||
field.type = FieldType_NumericU32;
|
||||
|
||||
field.value_u32 = value_u32;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ContextRecord::Add(FieldId field_id, u64 value_u64) {
|
||||
R_UNLESS(m_ctx.field_count < FieldsPerContext, erpt::ResultOutOfFieldSpace());
|
||||
|
||||
s_record_count++;
|
||||
auto &field = m_ctx.fields[m_ctx.field_count++];
|
||||
|
||||
field.id = field_id;
|
||||
field.type = FieldType_NumericU64;
|
||||
|
||||
field.value_u64 = value_u64;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ContextRecord::Add(FieldId field_id, s32 value_i32) {
|
||||
R_UNLESS(m_ctx.field_count < FieldsPerContext, erpt::ResultOutOfFieldSpace());
|
||||
|
||||
s_record_count++;
|
||||
auto &field = m_ctx.fields[m_ctx.field_count++];
|
||||
|
||||
field.id = field_id;
|
||||
field.type = FieldType_NumericI32;
|
||||
|
||||
field.value_i32 = value_i32;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ContextRecord::Add(FieldId field_id, s64 value_i64) {
|
||||
R_UNLESS(m_ctx.field_count < FieldsPerContext, erpt::ResultOutOfFieldSpace());
|
||||
|
||||
s_record_count++;
|
||||
auto &field = m_ctx.fields[m_ctx.field_count++];
|
||||
|
||||
field.id = field_id;
|
||||
field.type = FieldType_NumericI64;
|
||||
|
||||
field.value_i64 = value_i64;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ContextRecord::Add(FieldId field_id, const void *arr, u32 size, FieldType type) {
|
||||
R_UNLESS(m_ctx.field_count < FieldsPerContext, erpt::ResultOutOfFieldSpace());
|
||||
R_UNLESS(size <= m_ctx.array_free_count, erpt::ResultOutOfArraySpace());
|
||||
|
||||
const u32 start_idx = m_ctx.array_buffer_size - m_ctx.array_free_count;
|
||||
m_ctx.array_free_count -= size;
|
||||
|
||||
s_record_count++;
|
||||
auto &field = m_ctx.fields[m_ctx.field_count++];
|
||||
|
||||
field.id = field_id;
|
||||
field.type = type;
|
||||
|
||||
field.value_array = {
|
||||
.start_idx = start_idx,
|
||||
.size = size,
|
||||
};
|
||||
|
||||
std::memcpy(m_ctx.array_buffer + start_idx, arr, size);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ContextRecord::Add(FieldId field_id, const char *str, u32 str_size) {
|
||||
R_RETURN(this->Add(field_id, str, str_size, FieldType_String));
|
||||
}
|
||||
|
||||
Result ContextRecord::Add(FieldId field_id, const u8 *data, u32 size) {
|
||||
R_RETURN(this->Add(field_id, data, size, FieldType_U8Array));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* 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>
|
||||
#include "erpt_srv_allocator.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
class Context;
|
||||
|
||||
class ContextRecord : public Allocator {
|
||||
private:
|
||||
static u32 s_record_count;
|
||||
public:
|
||||
static u32 GetRecordCount() {
|
||||
return s_record_count;
|
||||
}
|
||||
private:
|
||||
ContextEntry m_ctx;
|
||||
private:
|
||||
Result Add(FieldId field_id, const void *arr, u32 size, FieldType type);
|
||||
public:
|
||||
ContextRecord();
|
||||
explicit ContextRecord(CategoryId category, u32 array_buf_size = ArrayBufferSizeDefault);
|
||||
~ContextRecord();
|
||||
|
||||
const ContextEntry *GetContextEntryPtr() const {
|
||||
return std::addressof(m_ctx);
|
||||
}
|
||||
|
||||
Result Initialize(const ContextEntry *ctx_ptr, const u8 *data, u32 data_size);
|
||||
|
||||
Result Add(FieldId field_id, bool value_bool);
|
||||
Result Add(FieldId field_id, u32 value_u32);
|
||||
Result Add(FieldId field_id, u64 value_u64);
|
||||
Result Add(FieldId field_id, s32 value_i32);
|
||||
Result Add(FieldId field_id, s64 value_i64);
|
||||
Result Add(FieldId field_id, const char *str, u32 str_size);
|
||||
Result Add(FieldId field_id, const u8 *data, u32 size);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,331 +0,0 @@
|
||||
/*
|
||||
* 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_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());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
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, erpt::srv::MakeNoCreateReportOptionFlags(), nullptr));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
os::Event *GetForcedShutdownUpdateEvent();
|
||||
|
||||
void InitializeForcedShutdownDetection();
|
||||
void FinalizeForcedShutdownDetection();
|
||||
|
||||
void SaveForcedShutdownContext();
|
||||
|
||||
void SubmitContextForForcedShutdownDetection(const ContextEntry *entry, const u8 *data, u32 data_size);
|
||||
|
||||
Result InvalidateForcedShutdownDetection();
|
||||
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
/*
|
||||
* 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>
|
||||
#include "erpt_srv_report.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
class Formatter {
|
||||
private:
|
||||
enum ElementSize {
|
||||
ElementSize_16 = 16,
|
||||
ElementSize_32 = 32,
|
||||
ElementSize_256 = 256,
|
||||
ElementSize_16384 = 16384,
|
||||
};
|
||||
private:
|
||||
static ValueTypeTag GetTag(s8) { return ValueTypeTag::I8; }
|
||||
static ValueTypeTag GetTag(s16) { return ValueTypeTag::I16; }
|
||||
static ValueTypeTag GetTag(s32) { return ValueTypeTag::I32; }
|
||||
static ValueTypeTag GetTag(s64) { return ValueTypeTag::I64; }
|
||||
static ValueTypeTag GetTag(u8) { return ValueTypeTag::U8; }
|
||||
static ValueTypeTag GetTag(u16) { return ValueTypeTag::U16; }
|
||||
static ValueTypeTag GetTag(u32) { return ValueTypeTag::U32; }
|
||||
static ValueTypeTag GetTag(u64) { return ValueTypeTag::U64; }
|
||||
|
||||
static Result AddStringValue(Report *report, const char *str, u32 len) {
|
||||
const u32 str_len = str != nullptr ? static_cast<u32>(strnlen(str, len)) : 0;
|
||||
|
||||
if (str_len < ElementSize_32) {
|
||||
R_TRY(report->Write(static_cast<u8>(static_cast<u8>(ValueTypeTag::FixStr) | str_len)));
|
||||
} else if (str_len < ElementSize_256) {
|
||||
R_TRY(report->Write(static_cast<u8>(ValueTypeTag::Str8)));
|
||||
R_TRY(report->Write(static_cast<u8>(str_len)));
|
||||
} else {
|
||||
R_UNLESS(str_len < ElementSize_16384, erpt::ResultFormatterError());
|
||||
R_TRY(report->Write(static_cast<u8>(ValueTypeTag::Str16)));
|
||||
|
||||
u16 be_str_len;
|
||||
util::StoreBigEndian(std::addressof(be_str_len), static_cast<u16>(str_len));
|
||||
R_TRY(report->Write(be_str_len));
|
||||
}
|
||||
|
||||
R_TRY(report->Write(str, str_len));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
static Result AddId(Report *report, FieldId field_id) {
|
||||
static_assert(MaxFieldStringSize < ElementSize_256);
|
||||
|
||||
const auto index = FindFieldIndex(field_id);
|
||||
AMS_ASSERT(index.has_value());
|
||||
|
||||
R_TRY(AddStringValue(report, FieldString[index.value()], strnlen(FieldString[index.value()], MaxFieldStringSize)));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static Result AddValue(Report *report, T value) {
|
||||
const u8 tag = static_cast<u8>(GetTag(value));
|
||||
|
||||
T big_endian_value;
|
||||
util::StoreBigEndian(std::addressof(big_endian_value), value);
|
||||
|
||||
R_TRY(report->Write(tag));
|
||||
R_TRY(report->Write(reinterpret_cast<u8 *>(std::addressof(big_endian_value)), sizeof(big_endian_value)));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static Result AddValueArray(Report *report, T *arr, u32 arr_size) {
|
||||
if (arr_size < ElementSize_16) {
|
||||
R_TRY(report->Write(static_cast<u8>(static_cast<u8>(ValueTypeTag::FixArray) | arr_size)));
|
||||
} else {
|
||||
R_UNLESS(arr_size < ElementSize_16384, erpt::ResultFormatterError());
|
||||
|
||||
u16 be_arr_size;
|
||||
util::StoreBigEndian(std::addressof(be_arr_size), static_cast<u16>(arr_size));
|
||||
|
||||
R_TRY(report->Write(static_cast<u8>(ValueTypeTag::Array16)));
|
||||
R_TRY(report->Write(be_arr_size));
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < arr_size; i++) {
|
||||
R_TRY(AddValue(report, arr[i]));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static Result AddIdValuePair(Report *report, FieldId field_id, T value) {
|
||||
R_TRY(AddId(report, field_id));
|
||||
R_TRY(AddValue(report, value));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static Result AddIdValueArray(Report *report, FieldId field_id, T *arr, u32 arr_size) {
|
||||
R_TRY(AddId(report, field_id));
|
||||
R_TRY(AddValueArray(report, arr, arr_size));
|
||||
R_SUCCEED();
|
||||
}
|
||||
public:
|
||||
static Result Begin(Report *report, u32 record_count) {
|
||||
if (record_count < ElementSize_16) {
|
||||
R_TRY(report->Write(static_cast<u8>(static_cast<u8>(ValueTypeTag::FixMap) | record_count)));
|
||||
} else {
|
||||
R_UNLESS(record_count < ElementSize_16384, erpt::ResultFormatterError());
|
||||
|
||||
u16 be_count;
|
||||
util::StoreBigEndian(std::addressof(be_count), static_cast<u16>(record_count));
|
||||
|
||||
R_TRY(report->Write(static_cast<u8>(ValueTypeTag::Map16)));
|
||||
R_TRY(report->Write(be_count));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
static Result End(Report *report) {
|
||||
AMS_UNUSED(report);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static Result AddField(Report *report, FieldId field_id, T value) {
|
||||
R_RETURN(AddIdValuePair<T>(report, field_id, value));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static Result AddField(Report *report, FieldId field_id, T *arr, u32 arr_size) {
|
||||
R_RETURN(AddIdValueArray(report, field_id, arr, arr_size));
|
||||
}
|
||||
|
||||
static Result AddField(Report *report, FieldId field_id, bool value) {
|
||||
R_TRY(AddId(report, field_id));
|
||||
R_TRY(report->Write(static_cast<u8>(value ? ValueTypeTag::True : ValueTypeTag::False)));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
static Result AddField(Report *report, FieldId field_id, char *str, u32 len) {
|
||||
R_TRY(AddId(report, field_id));
|
||||
|
||||
R_TRY(AddStringValue(report, str, len));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
static Result AddField(Report *report, FieldId field_id, u8 *bin, u32 len) {
|
||||
R_TRY(AddId(report, field_id));
|
||||
|
||||
if (len < ElementSize_256) {
|
||||
R_TRY(report->Write(static_cast<u8>(ValueTypeTag::Bin8)));
|
||||
R_TRY(report->Write(static_cast<u8>(len)));
|
||||
} else {
|
||||
R_UNLESS(len < ElementSize_16384, erpt::ResultFormatterError());
|
||||
R_TRY(report->Write(static_cast<u8>(ValueTypeTag::Bin16)));
|
||||
|
||||
u16 be_len;
|
||||
util::StoreBigEndian(std::addressof(be_len), static_cast<u16>(len));
|
||||
R_TRY(report->Write(be_len));
|
||||
}
|
||||
|
||||
R_TRY(report->Write(bin, len));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
Result SubmitFsInfo();
|
||||
|
||||
}
|
||||
@@ -1,499 +0,0 @@
|
||||
/*
|
||||
* 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_fs_info.hpp"
|
||||
#include "erpt_srv_context_record.hpp"
|
||||
#include "erpt_srv_context.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
namespace {
|
||||
|
||||
Result SubmitMmcDetailInfo() {
|
||||
/* Submit the mmc cid. */
|
||||
{
|
||||
u8 mmc_cid[fs::MmcCidSize] = {};
|
||||
if (R_SUCCEEDED(fs::GetMmcCid(mmc_cid, sizeof(mmc_cid)))) {
|
||||
/* Clear the serial number from the cid. */
|
||||
fs::ClearMmcCidSerialNumber(mmc_cid);
|
||||
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_NANDTypeInfo, sizeof(mmc_cid));
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add the cid. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDType, mmc_cid, sizeof(mmc_cid)));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Submit the mmc speed mode. */
|
||||
{
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_NANDSpeedModeInfo, 0x20);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Get the speed mode. */
|
||||
fs::MmcSpeedMode speed_mode{};
|
||||
const auto res = fs::GetMmcSpeedMode(std::addressof(speed_mode));
|
||||
if (R_SUCCEEDED(res)) {
|
||||
const char *speed_mode_name = "None";
|
||||
switch (speed_mode) {
|
||||
case fs::MmcSpeedMode_Identification:
|
||||
speed_mode_name = "Identification";
|
||||
break;
|
||||
case fs::MmcSpeedMode_LegacySpeed:
|
||||
speed_mode_name = "LegacySpeed";
|
||||
break;
|
||||
case fs::MmcSpeedMode_HighSpeed:
|
||||
speed_mode_name = "HighSpeed";
|
||||
break;
|
||||
case fs::MmcSpeedMode_Hs200:
|
||||
speed_mode_name = "Hs200";
|
||||
break;
|
||||
case fs::MmcSpeedMode_Hs400:
|
||||
speed_mode_name = "Hs400";
|
||||
break;
|
||||
case fs::MmcSpeedMode_Unknown:
|
||||
speed_mode_name = "Unknown";
|
||||
break;
|
||||
default:
|
||||
speed_mode_name = "UnDefined";
|
||||
break;
|
||||
}
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDSpeedMode, speed_mode_name, std::strlen(speed_mode_name)));
|
||||
} else {
|
||||
/* Getting speed mode failed, so add the result. */
|
||||
char res_str[0x20];
|
||||
util::SNPrintf(res_str, sizeof(res_str), "0x%08X", res.GetValue());
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDSpeedMode, res_str, std::strlen(res_str)));
|
||||
}
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
|
||||
/* Submit the mmc extended csd. */
|
||||
{
|
||||
u8 mmc_csd[fs::MmcExtendedCsdSize] = {};
|
||||
if (R_SUCCEEDED(fs::GetMmcExtendedCsd(mmc_csd, sizeof(mmc_csd)))) {
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_NANDExtendedCsd, 0);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add fields from the csd. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDPreEolInfo, static_cast<u32>(mmc_csd[fs::MmcExtendedCsdOffsetReEolInfo])));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDDeviceLifeTimeEstTypA, static_cast<u32>(mmc_csd[fs::MmcExtendedCsdOffsetDeviceLifeTimeEstTypA])));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDDeviceLifeTimeEstTypB, static_cast<u32>(mmc_csd[fs::MmcExtendedCsdOffsetDeviceLifeTimeEstTypB])));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Submit the mmc patrol count. */
|
||||
{
|
||||
u32 count = 0;
|
||||
if (R_SUCCEEDED(fs::GetMmcPatrolCount(std::addressof(count)))) {
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_NANDPatrolInfo, 0);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add the count. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDPatrolCount, count));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SubmitMmcErrorInfo() {
|
||||
/* Get the mmc error info. */
|
||||
fs::StorageErrorInfo sei = {};
|
||||
char log_buffer[erpt::ArrayBufferSizeDefault] = {};
|
||||
size_t log_size = 0;
|
||||
if (R_SUCCEEDED(fs::GetAndClearMmcErrorInfo(std::addressof(sei), std::addressof(log_size), log_buffer, sizeof(log_buffer)))) {
|
||||
/* Submit the error info. */
|
||||
{
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_NANDErrorInfo, 0);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add fields. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDNumActivationFailures, sei.num_activation_failures));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDNumActivationErrorCorrections, sei.num_activation_error_corrections));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDNumReadWriteFailures, sei.num_read_write_failures));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDNumReadWriteErrorCorrections, sei.num_read_write_error_corrections));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
|
||||
/* If we have a log, submit it. */
|
||||
if (log_size > 0) {
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_NANDDriverLog, log_size);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add fields. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_NANDErrorLog, log_buffer, log_size));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SubmitSdCardDetailInfo() {
|
||||
/* Submit the sd card cid. */
|
||||
{
|
||||
u8 sd_cid[fs::SdCardCidSize] = {};
|
||||
if (R_SUCCEEDED(fs::GetSdCardCid(sd_cid, sizeof(sd_cid)))) {
|
||||
/* Clear the serial number from the cid. */
|
||||
fs::ClearSdCardCidSerialNumber(sd_cid);
|
||||
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_MicroSDTypeInfo, sizeof(sd_cid));
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add the cid. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_MicroSDType, sd_cid, sizeof(sd_cid)));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Submit the sd card speed mode. */
|
||||
{
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_MicroSDSpeedModeInfo, 0x20);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Get the speed mode. */
|
||||
fs::SdCardSpeedMode speed_mode{};
|
||||
const auto res = fs::GetSdCardSpeedMode(std::addressof(speed_mode));
|
||||
if (R_SUCCEEDED(res)) {
|
||||
const char *speed_mode_name = "None";
|
||||
switch (speed_mode) {
|
||||
case fs::SdCardSpeedMode_Identification:
|
||||
speed_mode_name = "Identification";
|
||||
break;
|
||||
case fs::SdCardSpeedMode_DefaultSpeed:
|
||||
speed_mode_name = "DefaultSpeed";
|
||||
break;
|
||||
case fs::SdCardSpeedMode_HighSpeed:
|
||||
speed_mode_name = "HighSpeed";
|
||||
break;
|
||||
case fs::SdCardSpeedMode_Sdr12:
|
||||
speed_mode_name = "Sdr12";
|
||||
break;
|
||||
case fs::SdCardSpeedMode_Sdr25:
|
||||
speed_mode_name = "Sdr25";
|
||||
break;
|
||||
case fs::SdCardSpeedMode_Sdr50:
|
||||
speed_mode_name = "Sdr50";
|
||||
break;
|
||||
case fs::SdCardSpeedMode_Sdr104:
|
||||
speed_mode_name = "Sdr104";
|
||||
break;
|
||||
case fs::SdCardSpeedMode_Ddr50:
|
||||
speed_mode_name = "Ddr50";
|
||||
break;
|
||||
case fs::SdCardSpeedMode_Unknown:
|
||||
speed_mode_name = "Unknown";
|
||||
break;
|
||||
default:
|
||||
speed_mode_name = "UnDefined";
|
||||
break;
|
||||
}
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_MicroSDSpeedMode, speed_mode_name, std::strlen(speed_mode_name)));
|
||||
} else {
|
||||
/* Getting speed mode failed, so add the result. */
|
||||
char res_str[0x20];
|
||||
util::SNPrintf(res_str, sizeof(res_str), "0x%08X", res.GetValue());
|
||||
R_ABORT_UNLESS(record->Add(FieldId_MicroSDSpeedMode, res_str, std::strlen(res_str)));
|
||||
}
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
|
||||
/* Submit the area sizes. */
|
||||
{
|
||||
s64 user_area_size = 0;
|
||||
s64 prot_area_size = 0;
|
||||
const Result res_user = fs::GetSdCardUserAreaSize(std::addressof(user_area_size));
|
||||
const Result res_prot = fs::GetSdCardProtectedAreaSize(std::addressof(prot_area_size));
|
||||
if (R_SUCCEEDED(res_user) || R_SUCCEEDED(res_prot)) {
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_SdCardSizeSpec, 0);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add sizes. */
|
||||
if (R_SUCCEEDED(res_user)) {
|
||||
R_ABORT_UNLESS(record->Add(FieldId_SdCardUserAreaSize, user_area_size));
|
||||
}
|
||||
if (R_SUCCEEDED(res_prot)) {
|
||||
R_ABORT_UNLESS(record->Add(FieldId_SdCardProtectedAreaSize, prot_area_size));
|
||||
}
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SubmitSdCardErrorInfo() {
|
||||
/* Get the sd card error info. */
|
||||
fs::StorageErrorInfo sei = {};
|
||||
char log_buffer[erpt::ArrayBufferSizeDefault] = {};
|
||||
size_t log_size = 0;
|
||||
if (R_SUCCEEDED(fs::GetAndClearSdCardErrorInfo(std::addressof(sei), std::addressof(log_size), log_buffer, sizeof(log_buffer)))) {
|
||||
/* Submit the error info. */
|
||||
{
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_SdCardErrorInfo, 0);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add fields. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_SdCardNumActivationFailures, sei.num_activation_failures));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_SdCardNumActivationErrorCorrections, sei.num_activation_error_corrections));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_SdCardNumReadWriteFailures, sei.num_read_write_failures));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_SdCardNumReadWriteErrorCorrections, sei.num_read_write_error_corrections));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
|
||||
/* If we have a log, submit it. */
|
||||
if (log_size > 0) {
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_SdCardDriverLog, log_size);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add fields. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_SdCardErrorLog, log_buffer, log_size));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SubmitGameCardDetailInfo() {
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_GameCardCIDInfo, fs::GameCardCidSize + fs::GameCardDeviceIdSize);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add the game card cid. */
|
||||
{
|
||||
u8 gc_cid[fs::GameCardCidSize] = {};
|
||||
if (fs::IsGameCardInserted() && R_SUCCEEDED(fs::GetGameCardCid(gc_cid, sizeof(gc_cid)))) {
|
||||
/* Add the cid. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardCID, gc_cid, sizeof(gc_cid)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Add the game card device id. */
|
||||
{
|
||||
u8 gc_device_id[fs::GameCardDeviceIdSize] = {};
|
||||
if (fs::IsGameCardInserted() && R_SUCCEEDED(fs::GetGameCardDeviceId(gc_device_id, sizeof(gc_device_id)))) {
|
||||
/* Add the cid. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardDeviceId, gc_device_id, sizeof(gc_device_id)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SubmitGameCardErrorInfo() {
|
||||
/* Get the game card error info. */
|
||||
fs::GameCardErrorReportInfo ei = {};
|
||||
if (R_SUCCEEDED(fs::GetGameCardErrorReportInfo(std::addressof(ei)))) {
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_GameCardErrorInfo, 0);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add fields. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardCrcErrorCount, static_cast<u32>(ei.game_card_crc_error_num)));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardAsicCrcErrorCount, static_cast<u32>(ei.asic_crc_error_num)));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardRefreshCount, static_cast<u32>(ei.refresh_num)));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardReadRetryCount, static_cast<u32>(ei.retry_limit_out_num)));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardTimeoutRetryErrorCount, static_cast<u32>(ei.timeout_retry_num)));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardInsertionCount, static_cast<u32>(ei.insertion_count)));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardRemovalCount, static_cast<u32>(ei.removal_count)));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardAsicInitializeCount, ei.initialize_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardAsicReinitializeCount, ei.asic_reinitialize_num));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardAsicReinitializeFailureCount, ei.asic_reinitialize_failure_num));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardAsicReinitializeFailureDetail, ei.asic_reinitialize_failure_detail));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardRefreshSuccessCount, ei.refresh_succeeded_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardAwakenCount, ei.awaken_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardAwakenFailureCount, ei.awaken_failure_num));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardReadCountFromInsert, ei.read_count_from_insert));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardReadCountFromAwaken, ei.read_count_from_awaken));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardLastReadErrorPageAddress, ei.last_read_error_page_address));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_GameCardLastReadErrorPageCount, ei.last_read_error_page_count));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SubmitFileSystemErrorInfo() {
|
||||
/* Get the fsp error info. */
|
||||
fs::FileSystemProxyErrorInfo ei = {};
|
||||
if (R_SUCCEEDED(fs::GetAndClearFileSystemProxyErrorInfo(std::addressof(ei)))) {
|
||||
/* Submit FsProxyErrorInfo. */
|
||||
{
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_FsProxyErrorInfo, fat::FatErrorNameMaxLength);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add fields. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsRemountForDataCorruptCount, ei.rom_fs_remount_for_data_corruption_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsRemountForDataCorruptRetryOutCount, ei.rom_fs_unrecoverable_data_corruption_by_remount_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsError, ei.fat_fs_error.error));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsExtraError, ei.fat_fs_error.extra_error));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsErrorDrive, ei.fat_fs_error.drive_id));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsErrorName, ei.fat_fs_error.name, fat::FatErrorNameMaxLength));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsRecoveredByInvalidateCacheCount, ei.rom_fs_recovered_by_invalidate_cache_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsSaveDataIndexCount, ei.save_data_index_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisSystemFilePeakOpenCount, ei.bis_system_fat_report_info_1.open_file_peak_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisSystemDirectoryPeakOpenCount, ei.bis_system_fat_report_info_1.open_directory_peak_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisUserFilePeakOpenCount, ei.bis_user_fat_report_info_1.open_file_peak_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisUserDirectoryPeakOpenCount, ei.bis_user_fat_report_info_1.open_directory_peak_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsSdCardFilePeakOpenCount, ei.sd_card_fat_report_info_1.open_file_peak_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsSdCardDirectoryPeakOpenCount, ei.sd_card_fat_report_info_1.open_directory_peak_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisSystemUniqueFileEntryPeakOpenCount, ei.bis_system_fat_report_info_2.open_unique_file_entry_peak_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisSystemUniqueDirectoryEntryPeakOpenCount, ei.bis_system_fat_report_info_2.open_unique_directory_entry_peak_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisUserUniqueFileEntryPeakOpenCount, ei.bis_user_fat_report_info_2.open_unique_file_entry_peak_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisUserUniqueDirectoryEntryPeakOpenCount, ei.bis_user_fat_report_info_2.open_unique_directory_entry_peak_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsSdCardUniqueFileEntryPeakOpenCount, ei.sd_card_fat_report_info_2.open_unique_file_entry_peak_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsSdCardUniqueDirectoryEntryPeakOpenCount, ei.sd_card_fat_report_info_2.open_unique_directory_entry_peak_count));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
|
||||
/* Submit FsProxyErrorInfo2. */
|
||||
{
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_FsProxyErrorInfo2, 0);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add fields. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsDeepRetryStartCount, ei.rom_fs_deep_retry_start_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsUnrecoverableByGameCardAccessFailedCount, ei.rom_fs_unrecoverable_by_game_card_access_failed_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisSystemFatSafeControlResult, static_cast<u8>(ei.bis_system_fat_safe_info.result)));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisSystemFatErrorNumber, ei.bis_system_fat_safe_info.error_number));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisSystemFatSafeErrorNumber, ei.bis_system_fat_safe_info.safe_error_number));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisUserFatSafeControlResult, static_cast<u8>(ei.bis_user_fat_safe_info.result)));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisUserFatErrorNumber, ei.bis_user_fat_safe_info.error_number));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisUserFatSafeErrorNumber, ei.bis_user_fat_safe_info.safe_error_number));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SubmitMemoryReportInfo() {
|
||||
/* Get the memory report info. */
|
||||
fs::MemoryReportInfo mri = {};
|
||||
if (R_SUCCEEDED(fs::GetAndClearMemoryReportInfo(std::addressof(mri)))) {
|
||||
/* Create a record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_FsMemoryInfo, 0);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Add fields. */
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsPooledBufferPeakFreeSize, mri.pooled_buffer_peak_free_size));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsPooledBufferRetriedCount, mri.pooled_buffer_retried_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsPooledBufferReduceAllocationCount, mri.pooled_buffer_reduce_allocation_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsBufferManagerPeakFreeSize, mri.buffer_manager_peak_free_size));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsBufferManagerRetriedCount, mri.buffer_manager_retried_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsExpHeapPeakFreeSize, mri.exp_heap_peak_free_size));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsBufferPoolPeakFreeSize, mri.buffer_pool_peak_free_size));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsPatrolReadAllocateBufferSuccessCount, mri.patrol_read_allocate_buffer_success_count));
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsPatrolReadAllocateBufferFailureCount, mri.patrol_read_allocate_buffer_failure_count));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsBufferManagerPeakTotalAllocatableSize, mri.buffer_manager_peak_total_allocatable_size));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsBufferPoolMaxAllocateSize, mri.buffer_pool_max_allocate_size));
|
||||
|
||||
R_ABORT_UNLESS(record->Add(FieldId_FsPooledBufferFailedIdealAllocationCountOnAsyncAccess, mri.pooled_buffer_failed_ideal_allocation_count_on_async_access));
|
||||
|
||||
/* Submit the record. */
|
||||
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result SubmitFsInfo() {
|
||||
/* Temporarily disable auto-abort. */
|
||||
fs::ScopedAutoAbortDisabler aad;
|
||||
|
||||
/* Submit various FS info. */
|
||||
R_TRY(SubmitMmcDetailInfo());
|
||||
R_TRY(SubmitMmcErrorInfo());
|
||||
R_TRY(SubmitSdCardDetailInfo());
|
||||
R_TRY(SubmitSdCardErrorInfo());
|
||||
R_TRY(SubmitGameCardDetailInfo());
|
||||
R_TRY(SubmitGameCardErrorInfo());
|
||||
R_TRY(SubmitFileSystemErrorInfo());
|
||||
R_TRY(SubmitMemoryReportInfo());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* 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_journal.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
void Journal::CleanupAttachments() {
|
||||
return JournalForAttachments::CleanupAttachments();
|
||||
}
|
||||
|
||||
void Journal::CleanupReports() {
|
||||
return JournalForReports::CleanupReports();
|
||||
}
|
||||
|
||||
Result Journal::Commit() {
|
||||
/* Open the stream. */
|
||||
Stream stream;
|
||||
R_TRY(stream.OpenStream(JournalFileName, StreamMode_Write, JournalStreamBufferSize));
|
||||
|
||||
/* Commit the reports. */
|
||||
R_TRY(JournalForReports::CommitJournal(std::addressof(stream)));
|
||||
|
||||
/* Commit the meta. */
|
||||
R_TRY(JournalForMeta::CommitJournal(std::addressof(stream)));
|
||||
|
||||
/* Commit the attachments. */
|
||||
R_TRY(JournalForAttachments::CommitJournal(std::addressof(stream)));
|
||||
|
||||
/* Close and commit the stream. */
|
||||
stream.CloseStream();
|
||||
stream.CommitStream();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Journal::Delete(ReportId report_id) {
|
||||
R_RETURN(JournalForReports::DeleteReport(report_id));
|
||||
}
|
||||
|
||||
Result Journal::GetAttachmentList(u32 *out_count, AttachmentInfo *out_infos, size_t max_out_infos, ReportId report_id) {
|
||||
R_RETURN(JournalForAttachments::GetAttachmentList(out_count, out_infos, max_out_infos, report_id));
|
||||
}
|
||||
|
||||
util::Uuid Journal::GetJournalId() {
|
||||
return JournalForMeta::GetJournalId();
|
||||
}
|
||||
|
||||
s64 Journal::GetMaxReportSize() {
|
||||
return JournalForReports::GetMaxReportSize();
|
||||
}
|
||||
|
||||
Result Journal::GetReportList(ReportList *out, ReportType type_filter) {
|
||||
R_RETURN(JournalForReports::GetReportList(out, type_filter));
|
||||
}
|
||||
|
||||
u32 Journal::GetStoredReportCount(ReportType type) {
|
||||
return JournalForReports::GetStoredReportCount(type);
|
||||
}
|
||||
|
||||
u32 Journal::GetTransmittedCount(ReportType type) {
|
||||
return JournalForMeta::GetTransmittedCount(type);
|
||||
}
|
||||
|
||||
u32 Journal::GetUntransmittedCount(ReportType type) {
|
||||
return JournalForMeta::GetUntransmittedCount(type);
|
||||
}
|
||||
|
||||
u32 Journal::GetUsedStorage() {
|
||||
return JournalForReports::GetUsedStorage() + JournalForAttachments::GetUsedStorage();
|
||||
}
|
||||
|
||||
Result Journal::Restore() {
|
||||
/* Open the stream. */
|
||||
Stream stream;
|
||||
R_TRY(stream.OpenStream(JournalFileName, StreamMode_Read, JournalStreamBufferSize));
|
||||
|
||||
/* Restore the reports. */
|
||||
R_TRY(JournalForReports::RestoreJournal(std::addressof(stream)));
|
||||
|
||||
/* Restore the meta. */
|
||||
R_TRY(JournalForMeta::RestoreJournal(std::addressof(stream)));
|
||||
|
||||
/* Restore the attachments. */
|
||||
R_TRY(JournalForAttachments::RestoreJournal(std::addressof(stream)));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
JournalRecord<ReportInfo> *Journal::Retrieve(ReportId report_id) {
|
||||
return JournalForReports::RetrieveRecord(report_id);
|
||||
}
|
||||
|
||||
JournalRecord<AttachmentInfo> *Journal::Retrieve(AttachmentId attachment_id) {
|
||||
return JournalForAttachments::RetrieveRecord(attachment_id);
|
||||
}
|
||||
|
||||
Result Journal::Store(JournalRecord<ReportInfo> *record) {
|
||||
R_RETURN(JournalForReports::StoreRecord(record));
|
||||
}
|
||||
|
||||
Result Journal::Store(JournalRecord<AttachmentInfo> *record) {
|
||||
R_RETURN(JournalForAttachments::StoreRecord(record));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* 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>
|
||||
#include "erpt_srv_allocator.hpp"
|
||||
#include "erpt_srv_ref_count.hpp"
|
||||
#include "erpt_srv_journal_record.hpp"
|
||||
#include "erpt_srv_stream.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
constexpr inline s32 JournalVersion = 1;
|
||||
|
||||
constexpr inline u32 JournalStreamBufferSize = 4_KB;
|
||||
|
||||
struct JournalMeta {
|
||||
s32 version;
|
||||
u32 transmitted_count[ReportType_Count];
|
||||
u32 untransmitted_count[ReportType_Count];
|
||||
util::Uuid journal_id;
|
||||
u32 reserved[4];
|
||||
};
|
||||
static_assert(sizeof(JournalMeta) == 0x34);
|
||||
|
||||
class JournalForMeta {
|
||||
private:
|
||||
static JournalMeta s_journal_meta;
|
||||
public:
|
||||
static void InitializeJournal();
|
||||
static Result CommitJournal(Stream *stream);
|
||||
static Result RestoreJournal(Stream *stream);
|
||||
static u32 GetTransmittedCount(ReportType type);
|
||||
static u32 GetUntransmittedCount(ReportType type);
|
||||
static void IncrementCount(bool transmitted, ReportType type);
|
||||
static util::Uuid GetJournalId();
|
||||
};
|
||||
|
||||
class JournalForReports {
|
||||
private:
|
||||
using RecordListType = util::IntrusiveListBaseTraits<JournalRecord<ReportInfo>>::ListType;
|
||||
static RecordListType s_record_list;
|
||||
static u32 s_record_count;
|
||||
static u32 s_record_count_by_type[ReportType_Count];
|
||||
static u32 s_used_storage;
|
||||
private:
|
||||
static void EraseReportImpl(JournalRecord<ReportInfo> *record, bool increment_count, bool force_delete_attachments);
|
||||
public:
|
||||
static void CleanupReports();
|
||||
static Result CommitJournal(Stream *stream);
|
||||
static Result DeleteReport(ReportId report_id);
|
||||
static Result DeleteReportWithAttachments();
|
||||
static s64 GetMaxReportSize();
|
||||
static Result GetReportList(ReportList *out, ReportType type_filter);
|
||||
static u32 GetStoredReportCount(ReportType type);
|
||||
static u32 GetUsedStorage();
|
||||
static Result RestoreJournal(Stream *stream);
|
||||
|
||||
static JournalRecord<ReportInfo> *RetrieveRecord(ReportId report_id);
|
||||
static Result StoreRecord(JournalRecord<ReportInfo> *record);
|
||||
};
|
||||
|
||||
class JournalForAttachments {
|
||||
private:
|
||||
using AttachmentListType = util::IntrusiveListBaseTraits<JournalRecord<AttachmentInfo>>::ListType;
|
||||
static AttachmentListType s_attachment_list;
|
||||
static u32 s_attachment_count;
|
||||
static u32 s_used_storage;
|
||||
public:
|
||||
static void CleanupAttachments();
|
||||
static Result CommitJournal(Stream *stream);
|
||||
static Result DeleteAttachments(ReportId report_id);
|
||||
static Result GetAttachmentList(u32 *out_count, AttachmentInfo *out_infos, size_t max_out_infos, ReportId report_id);
|
||||
static u32 GetUsedStorage();
|
||||
static Result RestoreJournal(Stream *stream);
|
||||
|
||||
static JournalRecord<AttachmentInfo> *RetrieveRecord(AttachmentId attachment_id);
|
||||
static Result SetOwner(AttachmentId attachment_id, ReportId report_id);
|
||||
static Result StoreRecord(JournalRecord<AttachmentInfo> *record);
|
||||
|
||||
static Result SubmitAttachment(AttachmentId *out, char *name, const u8 *data, u32 data_size);
|
||||
};
|
||||
|
||||
class Journal {
|
||||
public:
|
||||
static void CleanupAttachments();
|
||||
static void CleanupReports();
|
||||
static Result Commit();
|
||||
static Result Delete(ReportId report_id);
|
||||
static Result GetAttachmentList(u32 *out_count, AttachmentInfo *out_infos, size_t max_out_infos, ReportId report_id);
|
||||
static util::Uuid GetJournalId();
|
||||
static s64 GetMaxReportSize();
|
||||
static Result GetReportList(ReportList *out, ReportType type_filter);
|
||||
static u32 GetStoredReportCount(ReportType type);
|
||||
static u32 GetTransmittedCount(ReportType type);
|
||||
static u32 GetUntransmittedCount(ReportType type);
|
||||
static u32 GetUsedStorage();
|
||||
static Result Restore();
|
||||
|
||||
static JournalRecord<ReportInfo> *Retrieve(ReportId report_id);
|
||||
static JournalRecord<AttachmentInfo> *Retrieve(AttachmentId attachment_id);
|
||||
|
||||
static Result Store(JournalRecord<ReportInfo> *record);
|
||||
static Result Store(JournalRecord<AttachmentInfo> *record);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,228 +0,0 @@
|
||||
/*
|
||||
* 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_journal.hpp"
|
||||
#include "erpt_srv_attachment.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
constinit util::IntrusiveListBaseTraits<JournalRecord<AttachmentInfo>>::ListType JournalForAttachments::s_attachment_list;
|
||||
constinit u32 JournalForAttachments::s_attachment_count = 0;
|
||||
constinit u32 JournalForAttachments::s_used_storage = 0;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline u32 AttachmentUsedStorageMax = 4_MB;
|
||||
|
||||
}
|
||||
|
||||
void JournalForAttachments::CleanupAttachments() {
|
||||
for (auto it = s_attachment_list.begin(); it != s_attachment_list.end(); /* ... */) {
|
||||
auto *record = std::addressof(*it);
|
||||
it = s_attachment_list.erase(s_attachment_list.iterator_to(*record));
|
||||
if (record->RemoveReference()) {
|
||||
Stream::DeleteStream(Attachment::FileName(record->m_info.attachment_id).name);
|
||||
delete record;
|
||||
}
|
||||
}
|
||||
AMS_ASSERT(s_attachment_list.empty());
|
||||
|
||||
s_attachment_count = 0;
|
||||
s_used_storage = 0;
|
||||
|
||||
}
|
||||
|
||||
Result JournalForAttachments::CommitJournal(Stream *stream) {
|
||||
R_TRY(stream->WriteStream(reinterpret_cast<const u8 *>(std::addressof(s_attachment_count)), sizeof(s_attachment_count)));
|
||||
for (auto it = s_attachment_list.crbegin(); it != s_attachment_list.crend(); it++) {
|
||||
R_TRY(stream->WriteStream(reinterpret_cast<const u8 *>(std::addressof(it->m_info)), sizeof(it->m_info)));
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result JournalForAttachments::DeleteAttachments(ReportId report_id) {
|
||||
for (auto it = s_attachment_list.begin(); it != s_attachment_list.end(); /* ... */) {
|
||||
auto *record = std::addressof(*it);
|
||||
if (record->m_info.owner_report_id == report_id) {
|
||||
/* Erase from the list. */
|
||||
it = s_attachment_list.erase(s_attachment_list.iterator_to(*record));
|
||||
|
||||
/* Update storage tracking counts. */
|
||||
--s_attachment_count;
|
||||
s_used_storage -= static_cast<u32>(record->m_info.attachment_size);
|
||||
|
||||
/* Delete the object, if we should. */
|
||||
if (record->RemoveReference()) {
|
||||
Stream::DeleteStream(Attachment::FileName(record->m_info.attachment_id).name);
|
||||
delete record;
|
||||
}
|
||||
} else {
|
||||
/* Not attached, just advance. */
|
||||
it++;
|
||||
}
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
u32 count = 0;
|
||||
for (auto it = s_attachment_list.cbegin(); it != s_attachment_list.cend() && count < max_out_infos; it++) {
|
||||
if (report_id == it->m_info.owner_report_id) {
|
||||
out_infos[count++] = it->m_info;
|
||||
}
|
||||
}
|
||||
*out_count = count;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
u32 JournalForAttachments::GetUsedStorage() {
|
||||
return s_used_storage;
|
||||
}
|
||||
|
||||
Result JournalForAttachments::RestoreJournal(Stream *stream) {
|
||||
/* Clear the used storage. */
|
||||
s_used_storage = 0;
|
||||
|
||||
/* Read the count from storage. */
|
||||
u32 read_size;
|
||||
u32 count;
|
||||
R_TRY(stream->ReadStream(std::addressof(read_size), reinterpret_cast<u8 *>(std::addressof(count)), sizeof(count)));
|
||||
|
||||
R_UNLESS(read_size == sizeof(count), erpt::ResultCorruptJournal());
|
||||
R_UNLESS(count <= AttachmentCountMax, erpt::ResultCorruptJournal());
|
||||
|
||||
/* If we fail in the middle of reading reports, we want to do cleanup. */
|
||||
auto cleanup_guard = SCOPE_GUARD { CleanupAttachments(); };
|
||||
|
||||
AttachmentInfo info;
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
R_TRY(stream->ReadStream(std::addressof(read_size), reinterpret_cast<u8 *>(std::addressof(info)), sizeof(info)));
|
||||
|
||||
R_UNLESS(read_size == sizeof(info), erpt::ResultCorruptJournal());
|
||||
|
||||
auto *record = new JournalRecord<AttachmentInfo>(info);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
auto record_guard = SCOPE_GUARD { delete record; };
|
||||
|
||||
if (R_FAILED(Stream::GetStreamSize(std::addressof(record->m_info.attachment_size), Attachment::FileName(record->m_info.attachment_id).name))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (record->m_info.flags.Test<AttachmentFlag::HasOwner>() && JournalForReports::RetrieveRecord(record->m_info.owner_report_id) != nullptr) {
|
||||
/* NOTE: Nintendo does not check the result of storing the new record... */
|
||||
record_guard.Cancel();
|
||||
StoreRecord(record);
|
||||
} else {
|
||||
/* If the attachment has no owner (or we deleted the report), delete the file associated with it. */
|
||||
Stream::DeleteStream(Attachment::FileName(record->m_info.attachment_id).name);
|
||||
}
|
||||
}
|
||||
|
||||
cleanup_guard.Cancel();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
JournalRecord<AttachmentInfo> *JournalForAttachments::RetrieveRecord(AttachmentId attachment_id) {
|
||||
for (auto it = s_attachment_list.begin(); it != s_attachment_list.end(); it++) {
|
||||
if (auto *record = std::addressof(*it); record->m_info.attachment_id == attachment_id) {
|
||||
return record;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Result JournalForAttachments::SetOwner(AttachmentId attachment_id, ReportId report_id) {
|
||||
for (auto it = s_attachment_list.begin(); it != s_attachment_list.end(); it++) {
|
||||
auto *record = std::addressof(*it);
|
||||
if (record->m_info.attachment_id == attachment_id) {
|
||||
R_UNLESS(!record->m_info.flags.Test<AttachmentFlag::HasOwner>(), erpt::ResultAlreadyOwned());
|
||||
|
||||
record->m_info.owner_report_id = report_id;
|
||||
record->m_info.flags.Set<AttachmentFlag::HasOwner>();
|
||||
R_SUCCEED();
|
||||
}
|
||||
}
|
||||
R_THROW(erpt::ResultInvalidArgument());
|
||||
}
|
||||
|
||||
Result JournalForAttachments::StoreRecord(JournalRecord<AttachmentInfo> *record) {
|
||||
/* Check if the record already exists. */
|
||||
for (auto it = s_attachment_list.begin(); it != s_attachment_list.end(); it++) {
|
||||
R_UNLESS(it->m_info.attachment_id != record->m_info.attachment_id, erpt::ResultAlreadyExists());
|
||||
}
|
||||
|
||||
/* Add a reference to the new record. */
|
||||
record->AddReference();
|
||||
|
||||
/* Push the record into the list. */
|
||||
s_attachment_list.push_front(*record);
|
||||
s_attachment_count++;
|
||||
s_used_storage += static_cast<u32>(record->m_info.attachment_size);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result JournalForAttachments::SubmitAttachment(AttachmentId *out, char *name, const u8 *data, u32 data_size) {
|
||||
R_UNLESS(data_size > 0, erpt::ResultInvalidArgument());
|
||||
R_UNLESS(data_size < AttachmentSizeMax, erpt::ResultInvalidArgument());
|
||||
|
||||
const auto name_len = std::strlen(name);
|
||||
R_UNLESS(name_len < AttachmentNameSizeMax, erpt::ResultInvalidArgument());
|
||||
|
||||
/* Ensure that we have free space. */
|
||||
while (s_used_storage > AttachmentUsedStorageMax) {
|
||||
R_TRY(JournalForReports::DeleteReportWithAttachments());
|
||||
}
|
||||
|
||||
AttachmentInfo info;
|
||||
info.attachment_id.uuid = util::GenerateUuid();
|
||||
info.flags = erpt::srv::MakeNoAttachmentFlags();
|
||||
info.attachment_size = data_size;
|
||||
util::Strlcpy(info.attachment_name, name, sizeof(info.attachment_name));
|
||||
|
||||
auto *record = new JournalRecord<AttachmentInfo>(info);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
record->AddReference();
|
||||
ON_SCOPE_EXIT {
|
||||
if (record->RemoveReference()) {
|
||||
delete record;
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
auto attachment = std::make_unique<Attachment>(record);
|
||||
R_UNLESS(attachment != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
R_TRY(attachment->Open(AttachmentOpenType_Create));
|
||||
ON_SCOPE_EXIT { attachment->Close(); };
|
||||
|
||||
R_TRY(attachment->Write(data, data_size));
|
||||
R_TRY(StoreRecord(record));
|
||||
}
|
||||
|
||||
*out = info.attachment_id;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* 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_journal.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
constinit JournalMeta JournalForMeta::s_journal_meta = {};
|
||||
|
||||
void JournalForMeta::InitializeJournal() {
|
||||
std::memset(std::addressof(s_journal_meta), 0, sizeof(s_journal_meta));
|
||||
s_journal_meta.journal_id = util::GenerateUuid();
|
||||
s_journal_meta.version = JournalVersion;
|
||||
}
|
||||
|
||||
Result JournalForMeta::CommitJournal(Stream *stream) {
|
||||
R_RETURN(stream->WriteStream(reinterpret_cast<const u8 *>(std::addressof(s_journal_meta)), sizeof(s_journal_meta)));
|
||||
}
|
||||
|
||||
Result JournalForMeta::RestoreJournal(Stream *stream) {
|
||||
u32 size;
|
||||
if (R_FAILED(stream->ReadStream(std::addressof(size), reinterpret_cast<u8 *>(std::addressof(s_journal_meta)), sizeof(s_journal_meta))) || size != sizeof(s_journal_meta)) {
|
||||
InitializeJournal();
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
u32 JournalForMeta::GetTransmittedCount(ReportType type) {
|
||||
if (ReportType_Start <= type && type < ReportType_End) {
|
||||
return s_journal_meta.transmitted_count[type];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
u32 JournalForMeta::GetUntransmittedCount(ReportType type) {
|
||||
if (ReportType_Start <= type && type < ReportType_End) {
|
||||
return s_journal_meta.untransmitted_count[type];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void JournalForMeta::IncrementCount(bool transmitted, ReportType type) {
|
||||
if (ReportType_Start <= type && type < ReportType_End) {
|
||||
if (transmitted) {
|
||||
s_journal_meta.transmitted_count[type]++;
|
||||
} else {
|
||||
s_journal_meta.untransmitted_count[type]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
util::Uuid JournalForMeta::GetJournalId() {
|
||||
return s_journal_meta.journal_id;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,228 +0,0 @@
|
||||
/*
|
||||
* 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_journal.hpp"
|
||||
#include "erpt_srv_report.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
constinit util::IntrusiveListBaseTraits<JournalRecord<ReportInfo>>::ListType JournalForReports::s_record_list;
|
||||
constinit u32 JournalForReports::s_record_count = 0;
|
||||
constinit u32 JournalForReports::s_record_count_by_type[ReportType_Count] = {};
|
||||
constinit u32 JournalForReports::s_used_storage = 0;
|
||||
|
||||
void JournalForReports::CleanupReports() {
|
||||
for (auto it = s_record_list.begin(); it != s_record_list.end(); /* ... */) {
|
||||
auto *record = std::addressof(*it);
|
||||
it = s_record_list.erase(s_record_list.iterator_to(*record));
|
||||
if (record->RemoveReference()) {
|
||||
Stream::DeleteStream(Report::FileName(record->m_info.id, false).name);
|
||||
delete record;
|
||||
}
|
||||
}
|
||||
AMS_ASSERT(s_record_list.empty());
|
||||
|
||||
s_record_count = 0;
|
||||
s_used_storage = 0;
|
||||
|
||||
std::memset(s_record_count_by_type, 0, sizeof(s_record_count_by_type));
|
||||
}
|
||||
|
||||
Result JournalForReports::CommitJournal(Stream *stream) {
|
||||
R_TRY(stream->WriteStream(reinterpret_cast<const u8 *>(std::addressof(s_record_count)), sizeof(s_record_count)));
|
||||
for (auto it = s_record_list.crbegin(); it != s_record_list.crend(); it++) {
|
||||
R_TRY(stream->WriteStream(reinterpret_cast<const u8 *>(std::addressof(it->m_info)), sizeof(it->m_info)));
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void JournalForReports::EraseReportImpl(JournalRecord<ReportInfo> *record, bool increment_count, bool force_delete_attachments) {
|
||||
/* Erase from the list. */
|
||||
s_record_list.erase(s_record_list.iterator_to(*record));
|
||||
|
||||
/* Update storage tracking counts. */
|
||||
--s_record_count;
|
||||
--s_record_count_by_type[record->m_info.type];
|
||||
s_used_storage -= static_cast<u32>(record->m_info.report_size);
|
||||
|
||||
/* If we should increment count, do so. */
|
||||
if (increment_count) {
|
||||
JournalForMeta::IncrementCount(record->m_info.flags.Test<ReportFlag::Transmitted>(), record->m_info.type);
|
||||
}
|
||||
|
||||
/* Delete any attachments. */
|
||||
if (force_delete_attachments || record->m_info.flags.Test<ReportFlag::HasAttachment>()) {
|
||||
JournalForAttachments::DeleteAttachments(record->m_info.id);
|
||||
}
|
||||
|
||||
/* Delete the object, if we should. */
|
||||
if (record->RemoveReference()) {
|
||||
Stream::DeleteStream(Report::FileName(record->m_info.id, false).name);
|
||||
delete record;
|
||||
}
|
||||
}
|
||||
|
||||
Result JournalForReports::DeleteReport(ReportId report_id) {
|
||||
for (auto it = s_record_list.begin(); it != s_record_list.end(); it++) {
|
||||
auto *record = std::addressof(*it);
|
||||
if (record->m_info.id == report_id) {
|
||||
EraseReportImpl(record, false, false);
|
||||
R_SUCCEED();
|
||||
}
|
||||
}
|
||||
R_THROW(erpt::ResultInvalidArgument());
|
||||
}
|
||||
|
||||
Result JournalForReports::DeleteReportWithAttachments() {
|
||||
for (auto it = s_record_list.rbegin(); it != s_record_list.rend(); it++) {
|
||||
auto *record = std::addressof(*it);
|
||||
if (record->m_info.flags.Test<ReportFlag::HasAttachment>()) {
|
||||
EraseReportImpl(record, true, true);
|
||||
R_SUCCEED();
|
||||
}
|
||||
}
|
||||
R_THROW(erpt::ResultNotFound());
|
||||
}
|
||||
|
||||
s64 JournalForReports::GetMaxReportSize() {
|
||||
s64 max_size = 0;
|
||||
for (auto it = s_record_list.begin(); it != s_record_list.end(); it++) {
|
||||
max_size = std::max(max_size, it->m_info.report_size);
|
||||
}
|
||||
return max_size;
|
||||
}
|
||||
|
||||
Result JournalForReports::GetReportList(ReportList *out, ReportType type_filter) {
|
||||
u32 count = 0;
|
||||
for (auto it = s_record_list.cbegin(); it != s_record_list.cend() && count < util::size(out->reports); it++) {
|
||||
if (type_filter == ReportType_Any || type_filter == it->m_info.type) {
|
||||
out->reports[count++] = it->m_info;
|
||||
}
|
||||
}
|
||||
out->report_count = count;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
u32 JournalForReports::GetStoredReportCount(ReportType type) {
|
||||
if (ReportType_Start <= type && type < ReportType_End) {
|
||||
return s_record_count_by_type[type];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
u32 JournalForReports::GetUsedStorage() {
|
||||
return s_used_storage;
|
||||
}
|
||||
|
||||
Result JournalForReports::RestoreJournal(Stream *stream) {
|
||||
/* Clear the used storage. */
|
||||
s_used_storage = 0;
|
||||
|
||||
/* Read the count from storage. */
|
||||
u32 read_size;
|
||||
u32 count;
|
||||
R_TRY(stream->ReadStream(std::addressof(read_size), reinterpret_cast<u8 *>(std::addressof(count)), sizeof(count)));
|
||||
|
||||
R_UNLESS(read_size == sizeof(count), erpt::ResultCorruptJournal());
|
||||
R_UNLESS(count <= ReportCountMax, erpt::ResultCorruptJournal());
|
||||
|
||||
/* If we fail in the middle of reading reports, we want to do cleanup. */
|
||||
auto cleanup_guard = SCOPE_GUARD { CleanupReports(); };
|
||||
|
||||
ReportInfo info;
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
R_TRY(stream->ReadStream(std::addressof(read_size), reinterpret_cast<u8 *>(std::addressof(info)), sizeof(info)));
|
||||
|
||||
R_UNLESS(read_size == sizeof(info), erpt::ResultCorruptJournal());
|
||||
R_UNLESS(ReportType_Start <= info.type, erpt::ResultCorruptJournal());
|
||||
R_UNLESS(info.type < ReportType_End, erpt::ResultCorruptJournal());
|
||||
|
||||
auto *record = new JournalRecord<ReportInfo>(info);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* NOTE: Nintendo does not ensure that the newly allocated record does not leak in the failure case. */
|
||||
/* We will ensure it is freed if we early error. */
|
||||
auto record_guard = SCOPE_GUARD { delete record; };
|
||||
|
||||
if (record->m_info.report_size == 0) {
|
||||
R_UNLESS(R_SUCCEEDED(Stream::GetStreamSize(std::addressof(record->m_info.report_size), Report::FileName(record->m_info.id, false).name)), erpt::ResultCorruptJournal());
|
||||
}
|
||||
|
||||
record_guard.Cancel();
|
||||
|
||||
/* NOTE: Nintendo does not check the result of storing the new record... */
|
||||
StoreRecord(record);
|
||||
}
|
||||
|
||||
cleanup_guard.Cancel();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
JournalRecord<ReportInfo> *JournalForReports::RetrieveRecord(ReportId report_id) {
|
||||
for (auto it = s_record_list.begin(); it != s_record_list.end(); it++) {
|
||||
if (auto *record = std::addressof(*it); record->m_info.id == report_id) {
|
||||
return record;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Result JournalForReports::StoreRecord(JournalRecord<ReportInfo> *record) {
|
||||
/* Check if the record already exists. */
|
||||
for (auto it = s_record_list.begin(); it != s_record_list.end(); it++) {
|
||||
R_UNLESS(it->m_info.id != record->m_info.id, erpt::ResultAlreadyExists());
|
||||
}
|
||||
|
||||
/* Delete an older report if we need to. */
|
||||
if (s_record_count >= ReportCountMax) {
|
||||
/* Nintendo deletes the oldest report from the type with the most reports. */
|
||||
/* This is an approximation of FIFO. */
|
||||
ReportType most_used_type = record->m_info.type;
|
||||
u32 most_used_count = s_record_count_by_type[most_used_type];
|
||||
|
||||
for (int i = ReportType_Start; i < ReportType_End; i++) {
|
||||
if (s_record_count_by_type[i] > most_used_count) {
|
||||
most_used_type = static_cast<ReportType>(i);
|
||||
most_used_count = s_record_count_by_type[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = s_record_list.rbegin(); it != s_record_list.rend(); it++) {
|
||||
if (it->m_info.type != most_used_type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
EraseReportImpl(std::addressof(*it), true, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
AMS_ASSERT(s_record_count < ReportCountMax);
|
||||
|
||||
/* Add a reference to the new record. */
|
||||
record->AddReference();
|
||||
|
||||
/* Push the record into the list. */
|
||||
s_record_list.push_front(*record);
|
||||
s_record_count++;
|
||||
s_record_count_by_type[record->m_info.type]++;
|
||||
s_used_storage += static_cast<u32>(record->m_info.report_size);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* 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>
|
||||
#include "erpt_srv_allocator.hpp"
|
||||
#include "erpt_srv_ref_count.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
template<typename Info>
|
||||
class JournalRecord : public Allocator, public RefCount, public util::IntrusiveListBaseNode<JournalRecord<Info>> {
|
||||
public:
|
||||
Info m_info;
|
||||
|
||||
JournalRecord() {
|
||||
std::memset(std::addressof(m_info), 0, sizeof(m_info));
|
||||
}
|
||||
|
||||
explicit JournalRecord(Info info) : m_info(info) { /* ... */ }
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* 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_keys.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const u8 PublicKeyModulusProduction[] = {
|
||||
0x00,
|
||||
0xAB, 0xB6, 0x6C, 0x43, 0x86, 0xDF, 0x52, 0x5F, 0xB9, 0xD3, 0x61, 0xEB, 0xFB, 0x16, 0x2B, 0x44,
|
||||
0xE1, 0x1F, 0xDD, 0x81, 0xFA, 0x20, 0x48, 0x7B, 0xDB, 0x17, 0x9F, 0x9A, 0x92, 0x1E, 0x0A, 0x80,
|
||||
0xD1, 0x1A, 0x4F, 0xD7, 0x49, 0xFE, 0xBA, 0x65, 0xE4, 0x61, 0x08, 0x26, 0x43, 0x7B, 0x4A, 0x16,
|
||||
0x59, 0x60, 0xD1, 0xE0, 0x42, 0x0A, 0x26, 0x54, 0xC4, 0xC7, 0x2A, 0xE3, 0x17, 0x1F, 0x8E, 0x35,
|
||||
0x79, 0xC0, 0x1B, 0xA1, 0xF8, 0x6F, 0x5C, 0xDC, 0x05, 0x2D, 0x90, 0x75, 0xD5, 0x98, 0x7E, 0x5A,
|
||||
0x07, 0x3F, 0x4E, 0x78, 0xF1, 0x69, 0x2F, 0xE9, 0x2E, 0x50, 0x01, 0x9F, 0xCE, 0x35, 0xC8, 0x4D,
|
||||
0x65, 0x23, 0xA8, 0x9F, 0xC3, 0x3C, 0x4A, 0xF2, 0x29, 0x4D, 0x10, 0x03, 0x1C, 0xB4, 0x0E, 0x64,
|
||||
0xB6, 0xDD, 0xB2, 0x74, 0xE3, 0x32, 0x84, 0x25, 0x99, 0xEA, 0xE1, 0x6C, 0x78, 0x24, 0xF2, 0xB0,
|
||||
0xD2, 0x2C, 0xA5, 0x1A, 0x70, 0xA6, 0x49, 0x08, 0x73, 0x8A, 0x74, 0x3A, 0x12, 0x0E, 0x1B, 0x68,
|
||||
0xD1, 0x6A, 0x6C, 0x3F, 0x2C, 0x2C, 0x53, 0xD5, 0xCE, 0x5A, 0x07, 0xA2, 0xB9, 0x2E, 0x0A, 0x77,
|
||||
0x51, 0x4B, 0xD2, 0x8E, 0x4F, 0xA3, 0xA8, 0x56, 0x99, 0x6F, 0x63, 0xBC, 0x23, 0x04, 0x6E, 0x71,
|
||||
0x57, 0x7C, 0xFD, 0x84, 0xA7, 0xF8, 0x8D, 0x7F, 0xD6, 0xA0, 0x6E, 0x92, 0xBC, 0xCC, 0x28, 0x82,
|
||||
0x60, 0xE9, 0x78, 0xC1, 0x31, 0x82, 0x4F, 0xF8, 0xC5, 0xDB, 0xB6, 0x6B, 0xF9, 0x62, 0x95, 0xD3,
|
||||
0xC8, 0x63, 0x59, 0x53, 0x3F, 0x82, 0xEB, 0x06, 0xA7, 0xB8, 0x55, 0xEC, 0x9E, 0x33, 0x04, 0xCF,
|
||||
0x5E, 0x42, 0x32, 0x09, 0x26, 0xFF, 0xB4, 0x5E, 0xBD, 0xD7, 0xA8, 0x6B, 0x2C, 0xF5, 0x68, 0x86,
|
||||
0xCD, 0x8A, 0x13, 0xF3, 0x1C, 0x5F, 0xE6, 0x4F, 0xFC, 0xD1, 0x07, 0x28, 0x5C, 0x2D, 0xA7, 0xF7
|
||||
};
|
||||
|
||||
constexpr const u8 PublicKeyModulusDevelopment[] = {
|
||||
0x00,
|
||||
0xAE, 0x7D, 0x6C, 0xD0, 0xC3, 0x13, 0x61, 0x01, 0x9D, 0x1B, 0x55, 0xA0, 0xE5, 0xF4, 0x3D, 0x56,
|
||||
0x7D, 0xCA, 0x0E, 0x49, 0xFB, 0x82, 0x08, 0x06, 0x33, 0xB6, 0x37, 0xB3, 0x4A, 0x3F, 0x57, 0x39,
|
||||
0x05, 0x84, 0x18, 0x3D, 0x82, 0xD8, 0x8F, 0xBC, 0xF3, 0xE1, 0x66, 0xEE, 0xD2, 0x80, 0x43, 0xF8,
|
||||
0xA7, 0xB7, 0x5E, 0x5B, 0x5C, 0xF9, 0x9D, 0x7F, 0xE9, 0x6C, 0x93, 0x8A, 0x65, 0xBB, 0xD1, 0xDD,
|
||||
0x56, 0xFF, 0x7C, 0x9D, 0x24, 0x66, 0x09, 0x84, 0x21, 0x2C, 0x7F, 0x0A, 0xB8, 0x31, 0x42, 0x29,
|
||||
0xE6, 0xD3, 0x20, 0x76, 0xA1, 0x1F, 0x7E, 0x59, 0x5B, 0x7C, 0xF6, 0xC6, 0x02, 0xDB, 0xC9, 0x1B,
|
||||
0xB9, 0x24, 0x99, 0xAD, 0x0F, 0x7B, 0x0D, 0x8E, 0x7E, 0x01, 0xFE, 0x95, 0xCE, 0x9B, 0xB5, 0x09,
|
||||
0xC5, 0xF5, 0xA5, 0x6A, 0x82, 0xF6, 0x57, 0xF8, 0x06, 0x72, 0xAE, 0x73, 0x71, 0xD1, 0x09, 0x2B,
|
||||
0xE2, 0x84, 0x0D, 0x66, 0x39, 0xB6, 0x21, 0x8B, 0x35, 0xE4, 0xDF, 0x90, 0x36, 0xE1, 0x3F, 0xC0,
|
||||
0x9F, 0xF8, 0x85, 0x03, 0xD6, 0xCA, 0xBB, 0x1A, 0x62, 0x2D, 0xE5, 0x03, 0xF6, 0x47, 0x00, 0x6E,
|
||||
0x98, 0x5A, 0x1C, 0x51, 0x94, 0x47, 0xF6, 0x83, 0x0C, 0x25, 0xBD, 0xBE, 0xBD, 0x6A, 0x35, 0xC0,
|
||||
0xAB, 0x65, 0xF8, 0x01, 0xF4, 0xC3, 0x2A, 0xA3, 0xBC, 0xD7, 0xD9, 0xF7, 0x2A, 0x98, 0x27, 0xE1,
|
||||
0x3F, 0x9A, 0xCF, 0xDF, 0xB1, 0x30, 0x82, 0xA4, 0xAA, 0x78, 0xCA, 0xC8, 0xB8, 0x34, 0xFA, 0xA7,
|
||||
0x75, 0x23, 0xC9, 0x9C, 0x11, 0x68, 0x7E, 0x0F, 0x80, 0x8F, 0x90, 0xA6, 0xDE, 0x2B, 0x47, 0x5B,
|
||||
0x94, 0x6F, 0xB9, 0x67, 0x4C, 0xC1, 0xAE, 0x50, 0x8F, 0xD8, 0xE3, 0xD1, 0xF2, 0x92, 0x54, 0x4C,
|
||||
0x25, 0x02, 0x5B, 0x31, 0x65, 0x5E, 0x41, 0x81, 0x34, 0xF4, 0xF1, 0x34, 0xE7, 0x64, 0x7A, 0xC1
|
||||
};
|
||||
|
||||
constexpr const u8 PublicKeyExponent[] = {
|
||||
0x01, 0x00, 0x01
|
||||
};
|
||||
|
||||
bool IsProductionModeImpl() {
|
||||
bool is_prod = true;
|
||||
if (settings::fwdbg::GetSettingsItemValue(std::addressof(is_prod), sizeof(is_prod), "erpt", "production_mode") != sizeof(is_prod)) {
|
||||
return true;
|
||||
}
|
||||
return is_prod;
|
||||
}
|
||||
|
||||
bool IsProductionMode() {
|
||||
AMS_FUNCTION_LOCAL_STATIC(bool, s_is_prod_mode, IsProductionModeImpl());
|
||||
|
||||
return s_is_prod_mode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const u8 *GetPublicKeyModulus() {
|
||||
return IsProductionMode() ? PublicKeyModulusProduction : PublicKeyModulusDevelopment;
|
||||
}
|
||||
|
||||
size_t GetPublicKeyModulusSize() {
|
||||
return IsProductionMode() ? sizeof(PublicKeyModulusProduction) : sizeof(PublicKeyModulusDevelopment);
|
||||
}
|
||||
|
||||
const u8 *GetPublicKeyExponent() {
|
||||
return PublicKeyExponent;
|
||||
}
|
||||
|
||||
size_t GetPublicKeyExponentSize() {
|
||||
return sizeof(PublicKeyExponent);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
const u8 *GetPublicKeyModulus();
|
||||
size_t GetPublicKeyModulusSize();
|
||||
|
||||
const u8 *GetPublicKeyExponent();
|
||||
size_t GetPublicKeyExponentSize();
|
||||
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
/*
|
||||
* 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_allocator.hpp"
|
||||
#include "erpt_srv_context_record.hpp"
|
||||
#include "erpt_srv_context.hpp"
|
||||
#include "erpt_srv_reporter.hpp"
|
||||
#include "erpt_srv_journal.hpp"
|
||||
#include "erpt_srv_service.hpp"
|
||||
#include "erpt_srv_forced_shutdown.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
constinit lmem::HeapHandle g_heap_handle;
|
||||
constinit ams::sf::ExpHeapAllocator g_sf_allocator = {};
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr fs::SystemSaveDataId SystemSaveDataId = 0x80000000000000D1;
|
||||
constexpr u32 SystemSaveDataFlags = fs::SaveDataFlags_KeepAfterResettingSystemSaveDataWithoutUserSaveData;
|
||||
constexpr s64 SystemSaveDataSize = 11_MB;
|
||||
constexpr s64 SystemSaveDataJournalSize = 2720_KB;
|
||||
|
||||
constinit bool g_automatic_report_cleanup_enabled = true;
|
||||
|
||||
Result ExtendSystemSaveData() {
|
||||
s64 cur_journal_size;
|
||||
s64 cur_savedata_size;
|
||||
|
||||
R_TRY(fs::GetSaveDataJournalSize(std::addressof(cur_journal_size), SystemSaveDataId));
|
||||
R_TRY(fs::GetSaveDataAvailableSize(std::addressof(cur_savedata_size), SystemSaveDataId));
|
||||
|
||||
if (cur_journal_size < SystemSaveDataJournalSize || cur_savedata_size < SystemSaveDataSize) {
|
||||
if (hos::GetVersion() >= hos::Version_3_0_0) {
|
||||
R_TRY(fs::ExtendSaveData(fs::SaveDataSpaceId::System, SystemSaveDataId, SystemSaveDataSize, SystemSaveDataJournalSize));
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MountSystemSaveData() {
|
||||
fs::DisableAutoSaveDataCreation();
|
||||
|
||||
/* Extend the system save data. */
|
||||
/* NOTE: Nintendo does not check result of this. */
|
||||
ExtendSystemSaveData();
|
||||
|
||||
R_TRY_CATCH(fs::MountSystemSaveData(ReportStoragePath, SystemSaveDataId)) {
|
||||
R_CATCH(fs::ResultTargetNotFound) {
|
||||
R_TRY(fs::CreateSystemSaveData(SystemSaveDataId, SystemSaveDataSize, SystemSaveDataJournalSize, SystemSaveDataFlags));
|
||||
R_TRY(fs::MountSystemSaveData(ReportStoragePath, SystemSaveDataId));
|
||||
}
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result Initialize(u8 *mem, size_t mem_size) {
|
||||
R_ABORT_UNLESS(time::Initialize());
|
||||
|
||||
g_heap_handle = lmem::CreateExpHeap(mem, mem_size, lmem::CreateOption_ThreadSafe);
|
||||
AMS_ABORT_UNLESS(g_heap_handle != nullptr);
|
||||
|
||||
fs::InitializeForSystem();
|
||||
fs::SetAllocator(Allocate, DeallocateWithSize);
|
||||
fs::SetEnabledAutoAbort(false);
|
||||
|
||||
R_ABORT_UNLESS(fs::MountSdCardErrorReportDirectoryForAtmosphere(ReportOnSdStoragePath));
|
||||
|
||||
if (g_automatic_report_cleanup_enabled) {
|
||||
constexpr s64 MinimumReportCountForCleanup = 1000;
|
||||
s64 report_count = MinimumReportCountForCleanup;
|
||||
|
||||
fs::DirectoryHandle dir;
|
||||
if (R_SUCCEEDED(fs::OpenDirectory(std::addressof(dir), ReportOnSdStorageRootDirectoryPath, fs::OpenDirectoryMode_All))) {
|
||||
ON_SCOPE_EXIT { fs::CloseDirectory(dir); };
|
||||
|
||||
if (R_FAILED(fs::GetDirectoryEntryCount(std::addressof(report_count), dir))) {
|
||||
report_count = MinimumReportCountForCleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (report_count >= MinimumReportCountForCleanup) {
|
||||
fs::CleanDirectoryRecursively(ReportOnSdStorageRootDirectoryPath);
|
||||
}
|
||||
}
|
||||
|
||||
R_ABORT_UNLESS(MountSystemSaveData());
|
||||
|
||||
g_sf_allocator.Attach(g_heap_handle);
|
||||
|
||||
for (const auto category_id : CategoryIndexToCategoryIdMap) {
|
||||
Context *ctx = new Context(category_id);
|
||||
AMS_ABORT_UNLESS(ctx != nullptr);
|
||||
}
|
||||
|
||||
Journal::Restore();
|
||||
|
||||
Reporter::UpdatePowerOnTime();
|
||||
Reporter::UpdateAwakeTime();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result InitializeAndStartService() {
|
||||
/* Initialize forced shutdown detection. */
|
||||
/* NOTE: Nintendo does not check error code here. */
|
||||
InitializeForcedShutdownDetection();
|
||||
|
||||
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 SetProductModel(const char *model, u32 model_len) {
|
||||
/* NOTE: Nintendo does not check that this allocation succeeds. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_ProductModelInfo);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
R_TRY(record->Add(FieldId_ProductModel, model, model_len));
|
||||
R_TRY(Context::SubmitContextRecord(std::move(record)));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetRegionSetting(const char *region, u32 region_len) {
|
||||
/* NOTE: Nintendo does not check that this allocation succeeds. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_RegionSettingInfo);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
R_TRY(record->Add(FieldId_RegionSetting, region, region_len));
|
||||
R_TRY(Context::SubmitContextRecord(std::move(record)));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetRedirectNewReportsToSdCard(bool redirect) {
|
||||
Reporter::SetRedirectNewReportsToSdCard(redirect);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetEnabledAutomaticReportCleanup(bool en) {
|
||||
g_automatic_report_cleanup_enabled = en;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void Wait() {
|
||||
/* Get the update event. */
|
||||
os::Event *event = GetForcedShutdownUpdateEvent();
|
||||
|
||||
/* Forever wait, saving any updates. */
|
||||
while (true) {
|
||||
event->Wait();
|
||||
event->Clear();
|
||||
SaveForcedShutdownContext();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* 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_manager_impl.hpp"
|
||||
#include "erpt_srv_journal.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
namespace {
|
||||
|
||||
using ManagerList = util::IntrusiveListBaseTraits<ManagerImpl>::ListType;
|
||||
|
||||
constinit ManagerList g_manager_list;
|
||||
|
||||
}
|
||||
|
||||
ManagerImpl::ManagerImpl() : m_system_event(os::EventClearMode_AutoClear, true) {
|
||||
g_manager_list.push_front(*this);
|
||||
}
|
||||
|
||||
ManagerImpl::~ManagerImpl() {
|
||||
g_manager_list.erase(g_manager_list.iterator_to(*this));
|
||||
}
|
||||
|
||||
void ManagerImpl::NotifyOne() {
|
||||
m_system_event.Signal();
|
||||
}
|
||||
|
||||
Result ManagerImpl::NotifyAll() {
|
||||
for (auto &manager : g_manager_list) {
|
||||
manager.NotifyOne();
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ManagerImpl::GetReportList(const ams::sf::OutBuffer &out_list, ReportType type_filter) {
|
||||
R_UNLESS(out_list.GetSize() == sizeof(ReportList), erpt::ResultInvalidArgument());
|
||||
|
||||
R_RETURN(Journal::GetReportList(reinterpret_cast<ReportList *>(out_list.GetPointer()), type_filter));
|
||||
}
|
||||
|
||||
Result ManagerImpl::GetEvent(ams::sf::OutCopyHandle out) {
|
||||
out.SetValue(m_system_event.GetReadableHandle(), false);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ManagerImpl::CleanupReports() {
|
||||
Journal::CleanupReports();
|
||||
Journal::CleanupAttachments();
|
||||
R_RETURN(Journal::Commit());
|
||||
}
|
||||
|
||||
Result ManagerImpl::DeleteReport(const ReportId &report_id) {
|
||||
R_TRY(Journal::Delete(report_id));
|
||||
R_RETURN(Journal::Commit());
|
||||
}
|
||||
|
||||
Result ManagerImpl::GetStorageUsageStatistics(ams::sf::Out<StorageUsageStatistics> out) {
|
||||
StorageUsageStatistics stats = {};
|
||||
|
||||
stats.journal_uuid = Journal::GetJournalId();
|
||||
stats.used_storage_size = Journal::GetUsedStorage();
|
||||
stats.max_report_size = Journal::GetMaxReportSize();
|
||||
|
||||
for (int i = ReportType_Start; i < ReportType_End; i++) {
|
||||
const auto type = static_cast<ReportType>(i);
|
||||
stats.report_count[i] = Journal::GetStoredReportCount(type);
|
||||
stats.transmitted_count[i] = Journal::GetTransmittedCount(type);
|
||||
stats.untransmitted_count[i] = Journal::GetUntransmittedCount(type);
|
||||
}
|
||||
|
||||
out.SetValue(stats);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ManagerImpl::GetAttachmentListDeprecated(const ams::sf::OutBuffer &out_list, const ReportId &report_id) {
|
||||
R_UNLESS(out_list.GetSize() == sizeof(AttachmentList), erpt::ResultInvalidArgument());
|
||||
|
||||
auto *attachment_list = reinterpret_cast<AttachmentList *>(out_list.GetPointer());
|
||||
|
||||
R_RETURN(Journal::GetAttachmentList(std::addressof(attachment_list->attachment_count), attachment_list->attachments, util::size(attachment_list->attachments), report_id));
|
||||
}
|
||||
|
||||
Result ManagerImpl::GetAttachmentList(ams::sf::Out<u32> out_count, const ams::sf::OutBuffer &out_buf, const ReportId &report_id) {
|
||||
R_RETURN(Journal::GetAttachmentList(out_count.GetPointer(), reinterpret_cast<AttachmentInfo *>(out_buf.GetPointer()), out_buf.GetSize() / sizeof(AttachmentInfo), report_id));
|
||||
}
|
||||
|
||||
Result ManagerImpl::GetReportSizeMax(ams::sf::Out<u32> out) {
|
||||
/* TODO: Where is this size defined? */
|
||||
constexpr size_t ReportSizeMax = 0x3FF4F;
|
||||
*out = ReportSizeMax;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* 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 ManagerImpl : public util::IntrusiveListBaseNode<ManagerImpl> {
|
||||
private:
|
||||
os::SystemEvent m_system_event;
|
||||
public:
|
||||
ManagerImpl();
|
||||
~ManagerImpl();
|
||||
private:
|
||||
void NotifyOne();
|
||||
public:
|
||||
static Result NotifyAll();
|
||||
public:
|
||||
Result GetReportList(const ams::sf::OutBuffer &out_list, ReportType type_filter);
|
||||
Result GetEvent(ams::sf::OutCopyHandle out);
|
||||
Result CleanupReports();
|
||||
Result DeleteReport(const ReportId &report_id);
|
||||
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 GetReportSizeMax(ams::sf::Out<u32> out);
|
||||
};
|
||||
static_assert(erpt::sf::IsIManager<ManagerImpl>);
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* 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 RefCount {
|
||||
private:
|
||||
static constexpr u32 MaxReferenceCount = 1000;
|
||||
std::atomic<u32> m_ref_count;
|
||||
public:
|
||||
RefCount() : m_ref_count(0) { /* ... */ }
|
||||
|
||||
void AddReference() {
|
||||
const auto prev = m_ref_count.fetch_add(1);
|
||||
AMS_ABORT_UNLESS(prev <= MaxReferenceCount);
|
||||
}
|
||||
|
||||
bool RemoveReference() {
|
||||
auto prev = m_ref_count.fetch_sub(1);
|
||||
AMS_ABORT_UNLESS(prev != 0);
|
||||
return prev == 1;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* 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_report_impl.hpp"
|
||||
#include "erpt_srv_report.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
ReportFileName Report::FileName(ReportId report_id, bool redirect_to_sd) {
|
||||
ReportFileName report_name;
|
||||
util::SNPrintf(report_name.name, sizeof(report_name.name),
|
||||
"%s:/%08x-%04x-%04x-%02x%02x-%04x%08x",
|
||||
(redirect_to_sd ? ReportOnSdStoragePath : ReportStoragePath),
|
||||
report_id.uuid_data.time_low,
|
||||
report_id.uuid_data.time_mid,
|
||||
report_id.uuid_data.time_high_and_version,
|
||||
report_id.uuid_data.clock_high,
|
||||
report_id.uuid_data.clock_low,
|
||||
static_cast<u32>((report_id.uuid_data.node >> BITSIZEOF(u32)) & 0x0000FFFF),
|
||||
static_cast<u32>((report_id.uuid_data.node >> 0) & 0xFFFFFFFF));
|
||||
return report_name;
|
||||
}
|
||||
|
||||
Report::Report(JournalRecord<ReportInfo> *r, bool redirect_to_sd) : m_record(r), m_redirect_to_sd_card(redirect_to_sd) {
|
||||
m_record->AddReference();
|
||||
}
|
||||
|
||||
Report::~Report() {
|
||||
this->CloseStream();
|
||||
if (m_record->RemoveReference()) {
|
||||
this->DeleteStream(this->FileName().name);
|
||||
delete m_record;
|
||||
}
|
||||
}
|
||||
|
||||
ReportFileName Report::FileName() const {
|
||||
return FileName(m_record->m_info.id, m_redirect_to_sd_card);
|
||||
}
|
||||
|
||||
Result Report::Open(ReportOpenType type) {
|
||||
switch (type) {
|
||||
case ReportOpenType_Create: R_RETURN(this->OpenStream(this->FileName().name, StreamMode_Write, ReportStreamBufferSize));
|
||||
case ReportOpenType_Read: R_RETURN(this->OpenStream(this->FileName().name, StreamMode_Read, ReportStreamBufferSize));
|
||||
default: R_THROW(erpt::ResultInvalidArgument());
|
||||
}
|
||||
}
|
||||
|
||||
Result Report::Read(u32 *out_read_count, u8 *dst, u32 dst_size) {
|
||||
R_RETURN(this->ReadStream(out_read_count, dst, dst_size));
|
||||
}
|
||||
|
||||
Result Report::Delete() {
|
||||
R_RETURN(this->DeleteStream(this->FileName().name));
|
||||
}
|
||||
|
||||
void Report::Close() {
|
||||
return this->CloseStream();
|
||||
}
|
||||
|
||||
Result Report::GetFlags(ReportFlagSet *out) const {
|
||||
*out = m_record->m_info.flags;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Report::SetFlags(ReportFlagSet flags) {
|
||||
if (((~m_record->m_info.flags) & flags).IsAnySet()) {
|
||||
m_record->m_info.flags |= flags;
|
||||
R_RETURN(Journal::Commit());
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Report::GetSize(s64 *out) const {
|
||||
R_RETURN(this->GetStreamSize(out));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* 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>
|
||||
#include "erpt_srv_allocator.hpp"
|
||||
#include "erpt_srv_stream.hpp"
|
||||
#include "erpt_srv_journal.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
enum ReportOpenType {
|
||||
ReportOpenType_Create = 0,
|
||||
ReportOpenType_Read = 1,
|
||||
};
|
||||
|
||||
constexpr inline u32 ReportStreamBufferSize = 1_KB;
|
||||
|
||||
class Report : public Allocator, public Stream {
|
||||
private:
|
||||
JournalRecord<ReportInfo> *m_record;
|
||||
bool m_redirect_to_sd_card;
|
||||
private:
|
||||
ReportFileName FileName() const;
|
||||
public:
|
||||
static ReportFileName FileName(ReportId report_id, bool redirect_to_sd);
|
||||
public:
|
||||
explicit Report(JournalRecord<ReportInfo> *r, bool redirect_to_sd);
|
||||
~Report();
|
||||
|
||||
Result Open(ReportOpenType type);
|
||||
Result Read(u32 *out_read_count, u8 *dst, u32 dst_size);
|
||||
Result Delete();
|
||||
void Close();
|
||||
|
||||
Result GetFlags(ReportFlagSet *out) const;
|
||||
Result SetFlags(ReportFlagSet flags);
|
||||
Result GetSize(s64 *out) const;
|
||||
|
||||
template<typename T>
|
||||
Result Write(T val) {
|
||||
R_RETURN(this->WriteStream(reinterpret_cast<const u8 *>(std::addressof(val)), sizeof(val)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Result Write(const T *buf, u32 buffer_size) {
|
||||
R_RETURN(this->WriteStream(reinterpret_cast<const u8 *>(buf), buffer_size));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* 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_report_impl.hpp"
|
||||
#include "erpt_srv_report.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
ReportImpl::ReportImpl() : m_report(nullptr) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
ReportImpl::~ReportImpl() {
|
||||
R_ABORT_UNLESS(this->Close());
|
||||
}
|
||||
|
||||
Result ReportImpl::Open(const ReportId &report_id) {
|
||||
R_UNLESS(m_report == nullptr, erpt::ResultAlreadyInitialized());
|
||||
|
||||
JournalRecord<ReportInfo> *record = Journal::Retrieve(report_id);
|
||||
R_UNLESS(record != nullptr, erpt::ResultNotFound());
|
||||
|
||||
m_report = new Report(record, false);
|
||||
R_UNLESS(m_report != nullptr, erpt::ResultOutOfMemory());
|
||||
auto report_guard = SCOPE_GUARD { delete m_report; m_report = nullptr; };
|
||||
|
||||
R_TRY(m_report->Open(ReportOpenType_Read));
|
||||
report_guard.Cancel();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ReportImpl::Read(ams::sf::Out<u32> out_count, const ams::sf::OutBuffer &out_buffer) {
|
||||
R_UNLESS(m_report != nullptr, erpt::ResultNotInitialized());
|
||||
|
||||
R_RETURN(m_report->Read(out_count.GetPointer(), static_cast<u8 *>(out_buffer.GetPointer()), static_cast<u32>(out_buffer.GetSize())));
|
||||
}
|
||||
|
||||
Result ReportImpl::SetFlags(ReportFlagSet flags) {
|
||||
R_UNLESS(m_report != nullptr, erpt::ResultNotInitialized());
|
||||
|
||||
R_RETURN(m_report->SetFlags(flags));
|
||||
}
|
||||
|
||||
Result ReportImpl::GetFlags(ams::sf::Out<ReportFlagSet> out) {
|
||||
R_UNLESS(m_report != nullptr, erpt::ResultNotInitialized());
|
||||
|
||||
R_RETURN(m_report->GetFlags(out.GetPointer()));
|
||||
}
|
||||
|
||||
Result ReportImpl::Close() {
|
||||
if (m_report != nullptr) {
|
||||
m_report->Close();
|
||||
delete m_report;
|
||||
m_report = nullptr;
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ReportImpl::GetSize(ams::sf::Out<s64> out) {
|
||||
R_UNLESS(m_report != nullptr, erpt::ResultNotInitialized());
|
||||
|
||||
R_RETURN(m_report->GetSize(out.GetPointer()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* 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 Report;
|
||||
|
||||
class ReportImpl {
|
||||
private:
|
||||
Report *m_report;
|
||||
public:
|
||||
ReportImpl();
|
||||
~ReportImpl();
|
||||
public:
|
||||
Result Open(const ReportId &report_id);
|
||||
Result Read(ams::sf::Out<u32> out_count, const ams::sf::OutBuffer &out_buffer);
|
||||
Result SetFlags(ReportFlagSet flags);
|
||||
Result GetFlags(ams::sf::Out<ReportFlagSet> out);
|
||||
Result Close();
|
||||
Result GetSize(ams::sf::Out<s64> out);
|
||||
};
|
||||
static_assert(erpt::sf::IsIReport<ReportImpl>);
|
||||
|
||||
}
|
||||
@@ -1,548 +0,0 @@
|
||||
/*
|
||||
* 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_reporter.hpp"
|
||||
#include "erpt_srv_report.hpp"
|
||||
#include "erpt_srv_journal.hpp"
|
||||
#include "erpt_srv_context_record.hpp"
|
||||
#include "erpt_srv_context.hpp"
|
||||
#include "erpt_srv_fs_info.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;
|
||||
constinit util::optional<time::SteadyClockTimePoint> Reporter::s_initial_launch_settings_completion_time;
|
||||
|
||||
namespace {
|
||||
|
||||
class AppletActiveTimeInfoList {
|
||||
private:
|
||||
struct AppletActiveTimeInfo {
|
||||
ncm::ProgramId program_id;
|
||||
os::Tick register_tick;
|
||||
TimeSpan suspended_duration;
|
||||
};
|
||||
static constexpr AppletActiveTimeInfo InvalidAppletActiveTimeInfo = { ncm::InvalidProgramId, os::Tick{}, TimeSpan::FromNanoSeconds(0) };
|
||||
private:
|
||||
std::array<AppletActiveTimeInfo, 8> m_list;
|
||||
public:
|
||||
constexpr AppletActiveTimeInfoList() : m_list{InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo} {
|
||||
m_list.fill(InvalidAppletActiveTimeInfo);
|
||||
}
|
||||
public:
|
||||
void Register(ncm::ProgramId program_id) {
|
||||
/* Find an unused entry. */
|
||||
auto entry = util::range::find_if(m_list, [](const AppletActiveTimeInfo &info) { return info.program_id == ncm::InvalidProgramId; });
|
||||
AMS_ASSERT(entry != m_list.end());
|
||||
|
||||
/* Create the entry. */
|
||||
*entry = { program_id, os::GetSystemTick(), TimeSpan::FromNanoSeconds(0) };
|
||||
}
|
||||
|
||||
void Unregister(ncm::ProgramId program_id) {
|
||||
/* Find a matching entry. */
|
||||
auto entry = util::range::find_if(m_list, [&](const AppletActiveTimeInfo &info) { return info.program_id == program_id; });
|
||||
AMS_ASSERT(entry != m_list.end());
|
||||
|
||||
/* Clear the entry. */
|
||||
*entry = InvalidAppletActiveTimeInfo;
|
||||
}
|
||||
|
||||
void UpdateSuspendedDuration(ncm::ProgramId program_id, TimeSpan suspended_duration) {
|
||||
/* Find a matching entry. */
|
||||
auto entry = util::range::find_if(m_list, [&](const AppletActiveTimeInfo &info) { return info.program_id == program_id; });
|
||||
AMS_ASSERT(entry != m_list.end());
|
||||
|
||||
/* Set the suspended duration. */
|
||||
entry->suspended_duration = suspended_duration;
|
||||
}
|
||||
|
||||
util::optional<TimeSpan> GetActiveDuration(ncm::ProgramId program_id) const {
|
||||
/* Try to find a matching entry. */
|
||||
const auto entry = util::range::find_if(m_list, [&](const AppletActiveTimeInfo &info) { return info.program_id == program_id; });
|
||||
if (entry != m_list.end()) {
|
||||
return (os::GetSystemTick() - entry->register_tick).ToTimeSpan() - entry->suspended_duration;
|
||||
} else {
|
||||
return util::nullopt;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
constinit AppletActiveTimeInfoList g_applet_active_time_info_list;
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
Result PullErrorContext(size_t *out_total_size, size_t *out_size, void *dst, size_t dst_size, const err::ContextDescriptor &descriptor, Result result) {
|
||||
s32 unk0;
|
||||
u32 total_size, size;
|
||||
R_TRY(::ectxrPullContext(std::addressof(unk0), std::addressof(total_size), std::addressof(size), dst, dst_size, descriptor.value, result.GetValue()));
|
||||
|
||||
*out_total_size = total_size;
|
||||
*out_size = size;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SubmitErrorContext(ContextRecord *record, Result result) {
|
||||
/* Only support submitting context on 11.x. */
|
||||
if (hos::GetVersion() < hos::Version_11_0_0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the context descriptor. */
|
||||
const auto descriptor = err::GetContextDescriptorFromResult(result);
|
||||
if (descriptor == err::InvalidContextDescriptor) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Pull the error context. */
|
||||
u8 error_context[0x200];
|
||||
size_t error_context_total_size;
|
||||
size_t error_context_size;
|
||||
if (R_FAILED(PullErrorContext(std::addressof(error_context_total_size), std::addressof(error_context_size), error_context, util::size(error_context), descriptor, result))) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the total size. */
|
||||
if (error_context_total_size == 0) {
|
||||
return;
|
||||
}
|
||||
record->Add(FieldId_ErrorContextTotalSize, error_context_total_size);
|
||||
|
||||
/* Set the context. */
|
||||
if (error_context_size == 0) {
|
||||
return;
|
||||
}
|
||||
record->Add(FieldId_ErrorContextSize, error_context_size);
|
||||
record->Add(FieldId_ErrorContext, error_context, error_context_size);
|
||||
}
|
||||
|
||||
constinit os::SdkMutex g_limit_mutex;
|
||||
constinit bool g_submitted_limit = false;
|
||||
|
||||
void SubmitResourceLimitLimitContext() {
|
||||
std::scoped_lock lk(g_limit_mutex);
|
||||
if (g_submitted_limit) {
|
||||
return;
|
||||
}
|
||||
|
||||
ON_SCOPE_EXIT { g_submitted_limit = true; };
|
||||
|
||||
/* Create and populate the record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_ResourceLimitLimitInfo);
|
||||
if (record == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
u64 reslimit_handle_value;
|
||||
if (R_FAILED(svc::GetInfo(std::addressof(reslimit_handle_value), svc::InfoType_ResourceLimit, svc::InvalidHandle, 0))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto handle = static_cast<svc::Handle>(reslimit_handle_value);
|
||||
ON_SCOPE_EXIT { R_ABORT_UNLESS(svc::CloseHandle(handle)); };
|
||||
|
||||
#define ADD_RESOURCE(__RESOURCE__) \
|
||||
do { \
|
||||
s64 limit_value; \
|
||||
if (R_FAILED(svc::GetResourceLimitLimitValue(std::addressof(limit_value), handle, svc::LimitableResource_##__RESOURCE__##Max))) { \
|
||||
return; \
|
||||
} \
|
||||
if (R_FAILED(record->Add(FieldId_System##__RESOURCE__##Limit, limit_value))) { \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
ADD_RESOURCE(PhysicalMemory);
|
||||
ADD_RESOURCE(ThreadCount);
|
||||
ADD_RESOURCE(EventCount);
|
||||
ADD_RESOURCE(TransferMemoryCount);
|
||||
ADD_RESOURCE(SessionCount);
|
||||
|
||||
#undef ADD_RESOURCE
|
||||
|
||||
Context::SubmitContextRecord(std::move(record));
|
||||
|
||||
g_submitted_limit = true;
|
||||
}
|
||||
|
||||
void SubmitResourceLimitPeakContext() {
|
||||
/* Create and populate the record. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_ResourceLimitPeakInfo);
|
||||
if (record == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
u64 reslimit_handle_value;
|
||||
if (R_FAILED(svc::GetInfo(std::addressof(reslimit_handle_value), svc::InfoType_ResourceLimit, svc::InvalidHandle, 0))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto handle = static_cast<svc::Handle>(reslimit_handle_value);
|
||||
ON_SCOPE_EXIT { R_ABORT_UNLESS(svc::CloseHandle(handle)); };
|
||||
|
||||
#define ADD_RESOURCE(__RESOURCE__) \
|
||||
do { \
|
||||
s64 peak_value; \
|
||||
if (R_FAILED(svc::GetResourceLimitPeakValue(std::addressof(peak_value), handle, svc::LimitableResource_##__RESOURCE__##Max))) { \
|
||||
return; \
|
||||
} \
|
||||
if (R_FAILED(record->Add(FieldId_System##__RESOURCE__##Peak, peak_value))) { \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
ADD_RESOURCE(PhysicalMemory);
|
||||
ADD_RESOURCE(ThreadCount);
|
||||
ADD_RESOURCE(EventCount);
|
||||
ADD_RESOURCE(TransferMemoryCount);
|
||||
ADD_RESOURCE(SessionCount);
|
||||
|
||||
#undef ADD_RESOURCE
|
||||
|
||||
Context::SubmitContextRecord(std::move(record));
|
||||
}
|
||||
|
||||
void SubmitResourceLimitContexts() {
|
||||
SubmitResourceLimitLimitContext();
|
||||
SubmitResourceLimitPeakContext();
|
||||
}
|
||||
#else
|
||||
void SubmitErrorContext(ContextRecord *record, Result result) {
|
||||
AMS_UNUSED(record, result);
|
||||
}
|
||||
#endif
|
||||
|
||||
Result ValidateCreateReportContext(const ContextEntry *ctx) {
|
||||
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());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SubmitReportDefaults(const ContextEntry *ctx) {
|
||||
AMS_ASSERT(ctx->category == CategoryId_ErrorInfo);
|
||||
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_ErrorInfoDefaults);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
bool found_abort_flag = false, found_syslog_flag = false;
|
||||
for (u32 i = 0; i < ctx->field_count; i++) {
|
||||
if (ctx->fields[i].id == FieldId_AbortFlag) {
|
||||
found_abort_flag = true;
|
||||
}
|
||||
if (ctx->fields[i].id == FieldId_HasSyslogFlag) {
|
||||
found_syslog_flag = true;
|
||||
}
|
||||
if (found_abort_flag && found_syslog_flag) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_abort_flag) {
|
||||
record->Add(FieldId_AbortFlag, false);
|
||||
}
|
||||
|
||||
if (!found_syslog_flag) {
|
||||
record->Add(FieldId_HasSyslogFlag, true);
|
||||
}
|
||||
|
||||
R_TRY(Context::SubmitContextRecord(std::move(record)));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SaveSyslogReportIfRequired(const ContextEntry *ctx, const ReportId &report_id) {
|
||||
bool needs_save_syslog = true;
|
||||
for (u32 i = 0; i < ctx->field_count; i++) {
|
||||
static_assert(FieldIndexToTypeMap[*FindFieldIndex(FieldId_HasSyslogFlag)] == FieldType_Bool);
|
||||
if (ctx->fields[i].id == FieldId_HasSyslogFlag && !ctx->fields[i].value_bool) {
|
||||
needs_save_syslog = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_save_syslog) {
|
||||
/* Here nintendo sends a report to srepo:u (vtable offset 0xE8) with data report_id. */
|
||||
/* We will not send report ids to srepo:u. */
|
||||
AMS_UNUSED(report_id);
|
||||
}
|
||||
}
|
||||
|
||||
void SubmitAppletActiveDurationForCrashReport(const ContextEntry *error_info_ctx, const void *data, u32 data_size, ContextRecord *error_info_auto_record) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(error_info_ctx != nullptr);
|
||||
AMS_ASSERT(error_info_ctx->category == CategoryId_ErrorInfo);
|
||||
AMS_ASSERT(data != nullptr);
|
||||
AMS_ASSERT(error_info_auto_record != nullptr);
|
||||
|
||||
/* Find the program id entry. */
|
||||
const auto fields_span = MakeSpan(error_info_ctx->fields, error_info_ctx->field_count);
|
||||
const auto program_id_entry = util::range::find_if(fields_span, [](const FieldEntry &entry) { return entry.id == FieldId_ProgramId; });
|
||||
if (program_id_entry == fields_span.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check that the report has abort flag set. */
|
||||
AMS_ASSERT(util::range::any_of(fields_span, [](const FieldEntry &entry) { return entry.id == FieldId_AbortFlag && entry.value_bool; }));
|
||||
|
||||
/* Check that the program id's value is a string. */
|
||||
AMS_ASSERT(program_id_entry->type == FieldType_String);
|
||||
|
||||
/* Check that the program id's length is valid/in bounds. */
|
||||
const auto program_id_ofs = program_id_entry->value_array.start_idx;
|
||||
const auto program_id_len = program_id_entry->value_array.size;
|
||||
AMS_ASSERT(16 <= program_id_len && program_id_len <= 17);
|
||||
AMS_ASSERT(program_id_ofs + program_id_len <= data_size);
|
||||
AMS_UNUSED(data_size);
|
||||
|
||||
/* Get the program id string. */
|
||||
char program_id_str[17];
|
||||
std::memcpy(program_id_str, static_cast<const u8 *>(data) + program_id_ofs, std::min<size_t>(program_id_len, sizeof(program_id_str)));
|
||||
program_id_str[sizeof(program_id_str) - 1] = '\x00';
|
||||
|
||||
/* Convert the string to an integer. */
|
||||
char *end_ptr = nullptr;
|
||||
const ncm::ProgramId program_id = { std::strtoull(program_id_str, std::addressof(end_ptr), 16) };
|
||||
AMS_ASSERT(*end_ptr == '\x00');
|
||||
|
||||
/* Get the active duration. */
|
||||
const auto active_duration = g_applet_active_time_info_list.GetActiveDuration(program_id);
|
||||
if (!active_duration.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Add the active applet time. */
|
||||
const auto result = error_info_auto_record->Add(FieldId_AppletTotalActiveTime, (*active_duration).GetSeconds());
|
||||
R_ASSERT(result);
|
||||
}
|
||||
|
||||
Result LinkAttachments(const ReportId &report_id, const AttachmentId *attachments, u32 num_attachments) {
|
||||
for (u32 i = 0; i < num_attachments; i++) {
|
||||
R_TRY(JournalForAttachments::SetOwner(attachments[i], report_id));
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CreateReportFile(const ReportId &report_id, ReportType type, const ReportMetaData *meta, u32 num_attachments, const time::PosixTime ×tamp_user, const time::PosixTime ×tamp_network, bool redirect_new_reports) {
|
||||
/* Define journal record deleter. */
|
||||
struct JournalRecordDeleter {
|
||||
void operator()(JournalRecord<ReportInfo> *record) {
|
||||
if (record != nullptr) {
|
||||
if (record->RemoveReference()) {
|
||||
delete record;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Make a journal record. */
|
||||
auto record = std::unique_ptr<JournalRecord<ReportInfo>, JournalRecordDeleter>{new JournalRecord<ReportInfo>, JournalRecordDeleter{}};
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
record->AddReference();
|
||||
|
||||
record->m_info.type = type;
|
||||
record->m_info.id = report_id;
|
||||
record->m_info.flags = erpt::srv::MakeNoReportFlags();
|
||||
record->m_info.timestamp_user = timestamp_user;
|
||||
record->m_info.timestamp_network = timestamp_network;
|
||||
if (meta != nullptr) {
|
||||
record->m_info.meta_data = *meta;
|
||||
}
|
||||
if (num_attachments > 0) {
|
||||
record->m_info.flags.Set<ReportFlag::HasAttachment>();
|
||||
}
|
||||
|
||||
auto report = std::make_unique<Report>(record.get(), redirect_new_reports);
|
||||
R_UNLESS(report != nullptr, erpt::ResultOutOfMemory());
|
||||
auto report_guard = SCOPE_GUARD { report->Delete(); };
|
||||
|
||||
R_TRY(Context::WriteContextsToReport(report.get()));
|
||||
R_TRY(report->GetSize(std::addressof(record->m_info.report_size)));
|
||||
|
||||
if (!redirect_new_reports) {
|
||||
/* If we're not redirecting new reports, then we want to store the report in the journal. */
|
||||
R_TRY(Journal::Store(record.get()));
|
||||
} else {
|
||||
/* If we are redirecting new reports, we don't want to store the report in the journal. */
|
||||
/* We should take this opportunity to delete any attachments associated with the report. */
|
||||
R_ABORT_UNLESS(JournalForAttachments::DeleteAttachments(report_id));
|
||||
}
|
||||
|
||||
R_TRY(Journal::Commit());
|
||||
|
||||
report_guard.Cancel();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result Reporter::RegisterRunningApplet(ncm::ProgramId program_id) {
|
||||
g_applet_active_time_info_list.Register(program_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Reporter::UnregisterRunningApplet(ncm::ProgramId program_id) {
|
||||
g_applet_active_time_info_list.Unregister(program_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Reporter::UpdateAppletSuspendedDuration(ncm::ProgramId program_id, TimeSpan duration) {
|
||||
g_applet_active_time_info_list.UpdateSuspendedDuration(program_id, duration);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Reporter::CreateReport(ReportType type, Result ctx_result, const ContextEntry *ctx, const u8 *data, u32 data_size, const ReportMetaData *meta, const AttachmentId *attachments, u32 num_attachments, erpt::CreateReportOptionFlagSet flags, const ReportId *specified_report_id) {
|
||||
/* Create a context record for the report. */
|
||||
auto record = std::make_unique<ContextRecord>();
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Initialize the record. */
|
||||
R_TRY(record->Initialize(ctx, data, data_size));
|
||||
|
||||
/* Create the report. */
|
||||
R_RETURN(CreateReport(type, ctx_result, std::move(record), meta, attachments, num_attachments, flags, specified_report_id));
|
||||
}
|
||||
|
||||
Result Reporter::CreateReport(ReportType type, Result ctx_result, std::unique_ptr<ContextRecord> record, const ReportMetaData *meta, const AttachmentId *attachments, u32 num_attachments, erpt::CreateReportOptionFlagSet flags, const ReportId *specified_report_id) {
|
||||
/* Clear the automatic categories, when we're done with our report. */
|
||||
ON_SCOPE_EXIT {
|
||||
Context::ClearContext(CategoryId_ErrorInfo);
|
||||
Context::ClearContext(CategoryId_ErrorInfoAuto);
|
||||
Context::ClearContext(CategoryId_ErrorInfoDefaults);
|
||||
};
|
||||
|
||||
/* Get the context entry pointer. */
|
||||
const ContextEntry *ctx = record->GetContextEntryPtr();
|
||||
|
||||
/* Validate the context. */
|
||||
R_TRY(ValidateCreateReportContext(ctx));
|
||||
|
||||
/* Submit report defaults. */
|
||||
R_TRY(SubmitReportDefaults(ctx));
|
||||
|
||||
/* Generate report id. */
|
||||
const ReportId report_id = specified_report_id ? *specified_report_id : ReportId{ .uuid = util::GenerateUuid() };
|
||||
|
||||
/* Get posix timestamps. */
|
||||
time::PosixTime timestamp_user;
|
||||
time::PosixTime timestamp_network;
|
||||
R_TRY(time::StandardUserSystemClock::GetCurrentTime(std::addressof(timestamp_user)));
|
||||
if (R_FAILED(time::StandardNetworkSystemClock::GetCurrentTime(std::addressof(timestamp_network)))) {
|
||||
timestamp_network = {};
|
||||
}
|
||||
|
||||
/* Save syslog report, if required. */
|
||||
SaveSyslogReportIfRequired(ctx, report_id);
|
||||
|
||||
/* Submit report contexts. */
|
||||
R_TRY(SubmitReportContexts(report_id, type, ctx_result, std::move(record), timestamp_user, timestamp_network, flags));
|
||||
|
||||
/* Link attachments to the report. */
|
||||
R_TRY(LinkAttachments(report_id, attachments, num_attachments));
|
||||
|
||||
/* Create the report file. */
|
||||
R_TRY(CreateReportFile(report_id, type, meta, num_attachments, timestamp_user, timestamp_network, s_redirect_new_reports));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Reporter::SubmitReportContexts(const ReportId &report_id, ReportType type, Result ctx_result, std::unique_ptr<ContextRecord> record, const time::PosixTime ×tamp_user, const time::PosixTime ×tamp_network, erpt::CreateReportOptionFlagSet flags) {
|
||||
/* Create automatic record. */
|
||||
auto auto_record = std::make_unique<ContextRecord>(CategoryId_ErrorInfoAuto, 0x300);
|
||||
R_UNLESS(auto_record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Handle error context. */
|
||||
if (R_FAILED(ctx_result)) {
|
||||
SubmitErrorContext(auto_record.get(), ctx_result);
|
||||
}
|
||||
|
||||
/* Collect unique report fields. */
|
||||
char identifier_str[0x40];
|
||||
report_id.uuid.ToString(identifier_str, sizeof(identifier_str));
|
||||
|
||||
const auto occurrence_tick = os::GetSystemTick();
|
||||
const s64 steady_clock_internal_offset_seconds = (hos::GetVersion() >= hos::Version_5_0_0) ? time::GetStandardSteadyClockInternalOffset().GetSeconds() : 0;
|
||||
|
||||
time::SteadyClockTimePoint steady_clock_current_timepoint;
|
||||
R_ABORT_UNLESS(time::GetStandardSteadyClockCurrentTimePoint(std::addressof(steady_clock_current_timepoint)));
|
||||
|
||||
/* Add automatic fields. */
|
||||
auto_record->Add(FieldId_OsVersion, s_os_version, util::Strnlen(s_os_version, sizeof(s_os_version)));
|
||||
auto_record->Add(FieldId_PrivateOsVersion, s_private_os_version, util::Strnlen(s_private_os_version, sizeof(s_private_os_version)));
|
||||
auto_record->Add(FieldId_SerialNumber, s_serial_number, util::Strnlen(s_serial_number, sizeof(s_serial_number)));
|
||||
auto_record->Add(FieldId_ReportIdentifier, identifier_str, util::Strnlen(identifier_str, sizeof(identifier_str)));
|
||||
auto_record->Add(FieldId_OccurrenceTimestamp, timestamp_user.value);
|
||||
auto_record->Add(FieldId_OccurrenceTimestampNet, timestamp_network.value);
|
||||
auto_record->Add(FieldId_ReportVisibilityFlag, type == ReportType_Visible);
|
||||
auto_record->Add(FieldId_OccurrenceTick, occurrence_tick.GetInt64Value());
|
||||
auto_record->Add(FieldId_SteadyClockInternalOffset, steady_clock_internal_offset_seconds);
|
||||
auto_record->Add(FieldId_SteadyClockCurrentTimePointValue, steady_clock_current_timepoint.value);
|
||||
auto_record->Add(FieldId_ElapsedTimeSincePowerOn, (occurrence_tick - *s_power_on_time).ToTimeSpan().GetSeconds());
|
||||
auto_record->Add(FieldId_ElapsedTimeSinceLastAwake, (occurrence_tick - *s_awake_time).ToTimeSpan().GetSeconds());
|
||||
|
||||
if (s_initial_launch_settings_completion_time) {
|
||||
s64 elapsed_seconds;
|
||||
if (R_SUCCEEDED(time::GetElapsedSecondsBetween(std::addressof(elapsed_seconds), *s_initial_launch_settings_completion_time, steady_clock_current_timepoint))) {
|
||||
auto_record->Add(FieldId_ElapsedTimeSinceInitialLaunch, elapsed_seconds);
|
||||
}
|
||||
}
|
||||
|
||||
if (s_application_launch_time) {
|
||||
auto_record->Add(FieldId_ApplicationAliveTime, (occurrence_tick - *s_application_launch_time).ToTimeSpan().GetSeconds());
|
||||
}
|
||||
|
||||
/* Submit applet active duration information. */
|
||||
{
|
||||
const auto *error_info_ctx = record->GetContextEntryPtr();
|
||||
SubmitAppletActiveDurationForCrashReport(error_info_ctx, error_info_ctx->array_buffer, error_info_ctx->array_buffer_size - error_info_ctx->array_free_count, auto_record.get());
|
||||
}
|
||||
|
||||
/* Submit the auto record. */
|
||||
R_TRY(Context::SubmitContextRecord(std::move(auto_record)));
|
||||
|
||||
/* Submit the info record. */
|
||||
R_TRY(Context::SubmitContextRecord(std::move(record)));
|
||||
|
||||
/* Submit context for resource limits. */
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
SubmitResourceLimitContexts();
|
||||
#endif
|
||||
|
||||
/* If we should, submit fs info. */
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
if (hos::GetVersion() >= hos::Version_17_0_0 && flags.Test<CreateReportOptionFlag::SubmitFsInfo>()) {
|
||||
/* NOTE: Nintendo ignores the result of this call. */
|
||||
SubmitFsInfo();
|
||||
}
|
||||
#else
|
||||
AMS_UNUSED(flags);
|
||||
#endif
|
||||
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* 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>
|
||||
#include "erpt_srv_context_record.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
class Reporter {
|
||||
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;
|
||||
static util::optional<time::SteadyClockTimePoint> s_initial_launch_settings_completion_time;
|
||||
public:
|
||||
static void ClearApplicationLaunchTime() { s_application_launch_time = util::nullopt; }
|
||||
static void ClearInitialLaunchSettingsCompletionTime() { s_initial_launch_settings_completion_time = util::nullopt; }
|
||||
|
||||
static void SetInitialLaunchSettingsCompletionTime(const time::SteadyClockTimePoint &time) { s_initial_launch_settings_completion_time = time; }
|
||||
|
||||
static void UpdateApplicationLaunchTime() { s_application_launch_time = os::GetSystemTick(); }
|
||||
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());
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
static Result RegisterRunningApplet(ncm::ProgramId program_id);
|
||||
static Result UnregisterRunningApplet(ncm::ProgramId program_id);
|
||||
static Result UpdateAppletSuspendedDuration(ncm::ProgramId program_id, TimeSpan duration);
|
||||
|
||||
static void SetRedirectNewReportsToSdCard(bool en) { s_redirect_new_reports = en; }
|
||||
private:
|
||||
static Result SubmitReportContexts(const ReportId &report_id, ReportType type, Result ctx_result, std::unique_ptr<ContextRecord> record, const time::PosixTime &user_timestamp, const time::PosixTime &network_timestamp, erpt::CreateReportOptionFlagSet flags);
|
||||
public:
|
||||
static Result CreateReport(ReportType type, Result ctx_result, const ContextEntry *ctx, const u8 *data, u32 data_size, const ReportMetaData *meta, const AttachmentId *attachments, u32 num_attachments, erpt::CreateReportOptionFlagSet flags, const ReportId *specified_report_id);
|
||||
static Result CreateReport(ReportType type, Result ctx_result, std::unique_ptr<ContextRecord> record, const ReportMetaData *meta, const AttachmentId *attachments, u32 num_attachments, erpt::CreateReportOptionFlagSet flags, const ReportId *specified_report_id);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* 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_service.hpp"
|
||||
#include "erpt_srv_context_impl.hpp"
|
||||
#include "erpt_srv_session_impl.hpp"
|
||||
#include "erpt_srv_stream.hpp"
|
||||
#include "erpt_srv_forced_shutdown.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
extern ams::sf::ExpHeapAllocator g_sf_allocator;
|
||||
|
||||
namespace {
|
||||
|
||||
struct ErrorReportServerOptions {
|
||||
static constexpr size_t PointerBufferSize = 0;
|
||||
static constexpr size_t MaxDomains = 64;
|
||||
static constexpr size_t MaxDomainObjects = 2 * ReportCountMax + 5 + 2;
|
||||
static constexpr bool CanDeferInvokeRequest = false;
|
||||
static constexpr bool CanManageMitmServers = false;
|
||||
};
|
||||
|
||||
constexpr inline size_t ErrorReportNumServers = 2;
|
||||
constexpr inline size_t ErrorReportReportSessions = 5;
|
||||
constexpr inline size_t ErrorReportContextSessions = 10;
|
||||
constexpr inline size_t ErrorReportMaxSessions = ErrorReportReportSessions + ErrorReportContextSessions;
|
||||
|
||||
constexpr inline sm::ServiceName ErrorReportContextServiceName = sm::ServiceName::Encode("erpt:c");
|
||||
constexpr inline sm::ServiceName ErrorReportReportServiceName = sm::ServiceName::Encode("erpt:r");
|
||||
|
||||
alignas(os::ThreadStackAlignment) constinit u8 g_server_thread_stack[16_KB];
|
||||
|
||||
enum PortIndex {
|
||||
PortIndex_Report,
|
||||
PortIndex_Context,
|
||||
};
|
||||
|
||||
class ErrorReportServiceManager : public ams::sf::hipc::ServerManager<ErrorReportNumServers, ErrorReportServerOptions, ErrorReportMaxSessions> {
|
||||
private:
|
||||
os::ThreadType m_thread;
|
||||
ams::sf::UnmanagedServiceObject<erpt::sf::IContext, erpt::srv::ContextImpl> m_context_session_object;
|
||||
private:
|
||||
static void ThreadFunction(void *_this) {
|
||||
reinterpret_cast<ErrorReportServiceManager *>(_this)->SetupAndLoopProcess();
|
||||
}
|
||||
|
||||
void SetupAndLoopProcess();
|
||||
|
||||
virtual Result OnNeedsToAccept(int port_index, Server *server) override {
|
||||
switch (port_index) {
|
||||
case PortIndex_Report:
|
||||
{
|
||||
auto intf = ams::sf::ObjectFactory<ams::sf::ExpHeapAllocator::Policy>::CreateSharedEmplaced<erpt::sf::ISession, erpt::srv::SessionImpl>(std::addressof(g_sf_allocator));
|
||||
AMS_ABORT_UNLESS(intf != nullptr);
|
||||
R_RETURN(this->AcceptImpl(server, intf));
|
||||
}
|
||||
case PortIndex_Context:
|
||||
R_RETURN(AcceptImpl(server, m_context_session_object.GetShared()));
|
||||
default:
|
||||
R_THROW(erpt::ResultNotSupported());
|
||||
}
|
||||
}
|
||||
public:
|
||||
Result Initialize() {
|
||||
R_ABORT_UNLESS(this->RegisterServer(PortIndex_Context, ErrorReportContextServiceName, ErrorReportContextSessions));
|
||||
R_ABORT_UNLESS(this->RegisterServer(PortIndex_Report, ErrorReportReportServiceName, ErrorReportReportSessions));
|
||||
|
||||
this->ResumeProcessing();
|
||||
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(m_thread), ThreadFunction, this, g_server_thread_stack, sizeof(g_server_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(erpt, IpcServer)));
|
||||
os::SetThreadNamePointer(std::addressof(m_thread), AMS_GET_SYSTEM_THREAD_NAME(erpt, IpcServer));
|
||||
|
||||
os::StartThread(std::addressof(m_thread));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void Wait() {
|
||||
os::WaitThread(std::addressof(m_thread));
|
||||
}
|
||||
};
|
||||
|
||||
void ErrorReportServiceManager::SetupAndLoopProcess() {
|
||||
const psc::PmModuleId dependencies[] = { psc::PmModuleId_Fs };
|
||||
psc::PmModule pm_module;
|
||||
psc::PmState pm_state;
|
||||
psc::PmFlagSet pm_flags;
|
||||
os::MultiWaitHolderType module_event_holder;
|
||||
|
||||
R_ABORT_UNLESS(pm_module.Initialize(psc::PmModuleId_Erpt, dependencies, util::size(dependencies), os::EventClearMode_ManualClear));
|
||||
|
||||
os::InitializeMultiWaitHolder(std::addressof(module_event_holder), pm_module.GetEventPointer()->GetBase());
|
||||
os::SetMultiWaitHolderUserData(std::addressof(module_event_holder), static_cast<uintptr_t>(psc::PmModuleId_Erpt));
|
||||
this->AddUserMultiWaitHolder(std::addressof(module_event_holder));
|
||||
|
||||
while (true) {
|
||||
/* NOTE: Nintendo checks the user holder data to determine what's signaled, we will prefer to just check the address. */
|
||||
auto *signaled_holder = this->WaitSignaled();
|
||||
if (signaled_holder != std::addressof(module_event_holder)) {
|
||||
R_ABORT_UNLESS(this->Process(signaled_holder));
|
||||
} else {
|
||||
pm_module.GetEventPointer()->Clear();
|
||||
if (R_SUCCEEDED(pm_module.GetRequest(std::addressof(pm_state), std::addressof(pm_flags)))) {
|
||||
switch (pm_state) {
|
||||
case psc::PmState_FullAwake:
|
||||
case psc::PmState_MinimumAwake:
|
||||
Stream::EnableFsAccess(true);
|
||||
break;
|
||||
case psc::PmState_ShutdownReady:
|
||||
FinalizeForcedShutdownDetection();
|
||||
[[fallthrough]];
|
||||
case psc::PmState_SleepReady:
|
||||
Stream::EnableFsAccess(false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
R_ABORT_UNLESS(pm_module.Acknowledge(pm_state, ResultSuccess()));
|
||||
} else {
|
||||
AMS_ASSERT(false);
|
||||
}
|
||||
this->AddUserMultiWaitHolder(signaled_holder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constinit util::TypedStorage<ErrorReportServiceManager> g_erpt_server_manager = {};
|
||||
|
||||
}
|
||||
|
||||
Result InitializeService() {
|
||||
util::ConstructAt(g_erpt_server_manager);
|
||||
R_RETURN(util::GetReference(g_erpt_server_manager).Initialize());
|
||||
}
|
||||
|
||||
void WaitService() {
|
||||
return util::GetReference(g_erpt_server_manager).Wait();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
Result InitializeService();
|
||||
void WaitService();
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* 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_session_impl.hpp"
|
||||
#include "erpt_srv_report_impl.hpp"
|
||||
#include "erpt_srv_manager_impl.hpp"
|
||||
#include "erpt_srv_attachment_impl.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
extern ams::sf::ExpHeapAllocator g_sf_allocator;
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename Interface, typename Impl>
|
||||
ALWAYS_INLINE Result OpenInterface(ams::sf::Out<ams::sf::SharedPointer<Interface>> &out) {
|
||||
/* Create an interface holder. */
|
||||
auto intf = ams::sf::ObjectFactory<ams::sf::ExpHeapAllocator::Policy>::CreateSharedEmplaced<Interface, Impl>(std::addressof(g_sf_allocator));
|
||||
R_UNLESS(intf != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Return it. */
|
||||
out.SetValue(intf);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result SessionImpl::OpenReport(ams::sf::Out<ams::sf::SharedPointer<erpt::sf::IReport>> out) {
|
||||
R_RETURN((OpenInterface<erpt::sf::IReport, ReportImpl>(out)));
|
||||
}
|
||||
|
||||
Result SessionImpl::OpenManager(ams::sf::Out<ams::sf::SharedPointer<erpt::sf::IManager>> out) {
|
||||
R_RETURN((OpenInterface<erpt::sf::IManager, ManagerImpl>(out)));
|
||||
}
|
||||
|
||||
Result SessionImpl::OpenAttachment(ams::sf::Out<ams::sf::SharedPointer<erpt::sf::IAttachment>> out) {
|
||||
R_RETURN((OpenInterface<erpt::sf::IAttachment, AttachmentImpl>(out)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* 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 SessionImpl {
|
||||
public:
|
||||
Result OpenReport(ams::sf::Out<ams::sf::SharedPointer<erpt::sf::IReport>> out);
|
||||
Result OpenManager(ams::sf::Out<ams::sf::SharedPointer<erpt::sf::IManager>> out);
|
||||
Result OpenAttachment(ams::sf::Out<ams::sf::SharedPointer<erpt::sf::IAttachment>> out);
|
||||
};
|
||||
static_assert(erpt::sf::IsISession<SessionImpl>);
|
||||
|
||||
}
|
||||
@@ -1,223 +0,0 @@
|
||||
/*
|
||||
* 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_allocator.hpp"
|
||||
#include "erpt_srv_stream.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
constinit bool Stream::s_can_access_fs = true;
|
||||
constinit os::SdkMutex Stream::s_fs_commit_mutex;
|
||||
|
||||
void Stream::EnableFsAccess(bool en) {
|
||||
s_can_access_fs = en;
|
||||
}
|
||||
|
||||
Result Stream::DeleteStream(const char *path) {
|
||||
R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState());
|
||||
R_RETURN(fs::DeleteFile(path));
|
||||
}
|
||||
|
||||
Result Stream::CommitStream() {
|
||||
R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState());
|
||||
|
||||
std::scoped_lock lk(s_fs_commit_mutex);
|
||||
|
||||
fs::CommitSaveData(ReportStoragePath);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Stream::GetStreamSize(s64 *out, const char *path) {
|
||||
R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState());
|
||||
|
||||
fs::FileHandle file;
|
||||
R_TRY(fs::OpenFile(std::addressof(file), path, fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
R_RETURN(fs::GetFileSize(out, file));
|
||||
}
|
||||
|
||||
Stream::Stream() : m_buffer_size(0), m_file_position(0), m_buffer_count(0), m_buffer(nullptr), m_stream_mode(StreamMode_Invalid), m_initialized(false) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
Stream::~Stream() {
|
||||
this->CloseStream();
|
||||
AMS_ASSERT(!s_fs_commit_mutex.IsLockedByCurrentThread());
|
||||
}
|
||||
|
||||
Result Stream::OpenStream(const char *path, StreamMode mode, u32 buffer_size) {
|
||||
R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState());
|
||||
R_UNLESS(!m_initialized, erpt::ResultAlreadyInitialized());
|
||||
|
||||
auto lock_guard = SCOPE_GUARD {
|
||||
if (s_fs_commit_mutex.IsLockedByCurrentThread()) {
|
||||
s_fs_commit_mutex.Unlock();
|
||||
}
|
||||
};
|
||||
|
||||
if (mode == StreamMode_Write) {
|
||||
s_fs_commit_mutex.Lock();
|
||||
|
||||
while (true) {
|
||||
R_TRY_CATCH(fs::OpenFile(std::addressof(m_file_handle), path, fs::OpenMode_Write | fs::OpenMode_AllowAppend)) {
|
||||
R_CATCH(fs::ResultPathNotFound) {
|
||||
R_TRY(fs::CreateFile(path, 0));
|
||||
continue;
|
||||
}
|
||||
} R_END_TRY_CATCH;
|
||||
break;
|
||||
}
|
||||
fs::SetFileSize(m_file_handle, 0);
|
||||
} else {
|
||||
R_UNLESS(mode == StreamMode_Read, erpt::ResultInvalidArgument());
|
||||
|
||||
R_TRY(fs::OpenFile(std::addressof(m_file_handle), path, fs::OpenMode_Read));
|
||||
}
|
||||
auto file_guard = SCOPE_GUARD { fs::CloseFile(m_file_handle); };
|
||||
|
||||
std::strncpy(m_file_name, path, sizeof(m_file_name));
|
||||
m_file_name[sizeof(m_file_name) - 1] = '\x00';
|
||||
|
||||
if (buffer_size > 0) {
|
||||
m_buffer = reinterpret_cast<u8 *>(Allocate(buffer_size));
|
||||
AMS_ASSERT(m_buffer != nullptr);
|
||||
} else {
|
||||
m_buffer = nullptr;
|
||||
}
|
||||
|
||||
|
||||
m_buffer_size = m_buffer != nullptr ? buffer_size : 0;
|
||||
m_buffer_count = 0;
|
||||
m_buffer_position = 0;
|
||||
m_file_position = 0;
|
||||
m_stream_mode = mode;
|
||||
m_initialized = true;
|
||||
|
||||
file_guard.Cancel();
|
||||
lock_guard.Cancel();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Stream::ReadStream(u32 *out, u8 *dst, u32 dst_size) {
|
||||
R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState());
|
||||
R_UNLESS(m_initialized, erpt::ResultNotInitialized());
|
||||
R_UNLESS(m_stream_mode == StreamMode_Read, erpt::ResultNotInitialized());
|
||||
R_UNLESS(out != nullptr, erpt::ResultInvalidArgument());
|
||||
R_UNLESS(dst != nullptr, erpt::ResultInvalidArgument());
|
||||
|
||||
size_t fs_read_size;
|
||||
u32 read_count = 0;
|
||||
|
||||
ON_SCOPE_EXIT {
|
||||
*out = read_count;
|
||||
};
|
||||
|
||||
if (m_buffer != nullptr) {
|
||||
while (dst_size > 0) {
|
||||
if (u32 cur = std::min<u32>(m_buffer_count - m_buffer_position, dst_size); cur > 0) {
|
||||
std::memcpy(dst, m_buffer + m_buffer_position, cur);
|
||||
m_buffer_position += cur;
|
||||
dst += cur;
|
||||
dst_size -= cur;
|
||||
read_count += cur;
|
||||
} else {
|
||||
R_TRY(fs::ReadFile(std::addressof(fs_read_size), m_file_handle, m_file_position, m_buffer, m_buffer_size));
|
||||
|
||||
m_buffer_position = 0;
|
||||
m_file_position += static_cast<u32>(fs_read_size);
|
||||
m_buffer_count = static_cast<u32>(fs_read_size);
|
||||
|
||||
if (m_buffer_count == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
R_TRY(fs::ReadFile(std::addressof(fs_read_size), m_file_handle, m_file_position, dst, dst_size));
|
||||
|
||||
m_file_position += static_cast<u32>(fs_read_size);
|
||||
read_count = static_cast<u32>(fs_read_size);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Stream::WriteStream(const u8 *src, u32 src_size) {
|
||||
R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState());
|
||||
R_UNLESS(m_initialized, erpt::ResultNotInitialized());
|
||||
R_UNLESS(m_stream_mode == StreamMode_Write, erpt::ResultNotInitialized());
|
||||
R_UNLESS(src != nullptr || src_size == 0, erpt::ResultInvalidArgument());
|
||||
|
||||
if (m_buffer != nullptr) {
|
||||
while (src_size > 0) {
|
||||
if (u32 cur = std::min<u32>(m_buffer_size - m_buffer_count, src_size); cur > 0) {
|
||||
std::memcpy(m_buffer + m_buffer_count, src, cur);
|
||||
m_buffer_count += cur;
|
||||
src += cur;
|
||||
src_size -= cur;
|
||||
}
|
||||
|
||||
if (m_buffer_count == m_buffer_size) {
|
||||
R_TRY(this->Flush());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
R_TRY(fs::WriteFile(m_file_handle, m_file_position, src, src_size, fs::WriteOption::None));
|
||||
m_file_position += src_size;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void Stream::CloseStream() {
|
||||
if (m_initialized) {
|
||||
if (s_can_access_fs) {
|
||||
if (m_stream_mode == StreamMode_Write) {
|
||||
this->Flush();
|
||||
fs::FlushFile(m_file_handle);
|
||||
}
|
||||
fs::CloseFile(m_file_handle);
|
||||
}
|
||||
|
||||
if (m_buffer != nullptr) {
|
||||
Deallocate(m_buffer);
|
||||
}
|
||||
|
||||
m_initialized = false;
|
||||
|
||||
if (s_fs_commit_mutex.IsLockedByCurrentThread()) {
|
||||
s_fs_commit_mutex.Unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result Stream::GetStreamSize(s64 *out) const {
|
||||
R_RETURN(GetStreamSize(out, m_file_name));
|
||||
}
|
||||
|
||||
Result Stream::Flush() {
|
||||
AMS_ASSERT(s_fs_commit_mutex.IsLockedByCurrentThread());
|
||||
|
||||
R_SUCCEED_IF(m_buffer_count == 0);
|
||||
R_TRY(fs::WriteFile(m_file_handle, m_file_position, m_buffer, m_buffer_count, fs::WriteOption::None));
|
||||
|
||||
m_file_position += m_buffer_count;
|
||||
m_buffer_count = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
enum StreamMode {
|
||||
StreamMode_Write = 0,
|
||||
StreamMode_Read = 1,
|
||||
StreamMode_Invalid = 2,
|
||||
};
|
||||
|
||||
class Stream {
|
||||
private:
|
||||
static bool s_can_access_fs;
|
||||
static os::SdkMutex s_fs_commit_mutex;
|
||||
private:
|
||||
u32 m_buffer_size;
|
||||
u32 m_file_position;
|
||||
u32 m_buffer_position;
|
||||
u32 m_buffer_count;
|
||||
u8 *m_buffer;
|
||||
StreamMode m_stream_mode;
|
||||
bool m_initialized;
|
||||
fs::FileHandle m_file_handle;
|
||||
char m_file_name[ReportFileNameLength];
|
||||
public:
|
||||
Stream();
|
||||
~Stream();
|
||||
|
||||
Result OpenStream(const char *path, StreamMode mode, u32 buffer_size);
|
||||
Result ReadStream(u32 *out, u8 *dst, u32 dst_size);
|
||||
Result WriteStream(const u8 *src, u32 src_size);
|
||||
void CloseStream();
|
||||
|
||||
Result GetStreamSize(s64 *out) const;
|
||||
private:
|
||||
Result Flush();
|
||||
public:
|
||||
static void EnableFsAccess(bool en);
|
||||
static Result DeleteStream(const char *path);
|
||||
static Result CommitStream();
|
||||
static Result GetStreamSize(s64 *out, const char *path);
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user