erpt: reimplement the sysmodule (#875)

* erpt: reimplement the sysmodule

* fatal: update for latest bindings

* erpt: amend logic for culling orphan attachments
This commit is contained in:
SciresM 2020-04-13 17:07:37 -07:00 committed by GitHub
parent eca5ac01b8
commit 79b9e07ee9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
117 changed files with 6716 additions and 59 deletions

View file

@ -20,47 +20,49 @@
#include <vapours.hpp> #include <vapours.hpp>
/* Libstratosphere-only utility. */ /* Libstratosphere-only utility. */
#include "stratosphere/util.hpp" #include <stratosphere/util.hpp>
/* Sadly required shims. */ /* Sadly required shims. */
#include "stratosphere/svc/svc_stratosphere_shims.hpp" #include <stratosphere/svc/svc_stratosphere_shims.hpp>
/* Critical modules with no dependencies. */ /* Critical modules with no dependencies. */
#include "stratosphere/ams.hpp" #include <stratosphere/ams.hpp>
#include "stratosphere/os.hpp" #include <stratosphere/os.hpp>
#include "stratosphere/dd.hpp" #include <stratosphere/dd.hpp>
#include "stratosphere/lmem.hpp" #include <stratosphere/lmem.hpp>
#include "stratosphere/mem.hpp" #include <stratosphere/mem.hpp>
/* Pull in all ID definitions from NCM. */ /* Pull in all ID definitions from NCM. */
#include "stratosphere/ncm/ncm_ids.hpp" #include <stratosphere/ncm/ncm_ids.hpp>
/* At this point, just include the rest alphabetically. */ /* At this point, just include the rest alphabetically. */
/* TODO: Figure out optimal order. */ /* TODO: Figure out optimal order. */
#include "stratosphere/boot2.hpp" #include <stratosphere/boot2.hpp>
#include "stratosphere/cfg.hpp" #include <stratosphere/cfg.hpp>
#include "stratosphere/dmnt.hpp" #include <stratosphere/dmnt.hpp>
#include "stratosphere/erpt.hpp" #include <stratosphere/erpt.hpp>
#include "stratosphere/fatal.hpp" #include <stratosphere/fatal.hpp>
#include "stratosphere/hid.hpp" #include <stratosphere/hid.hpp>
#include "stratosphere/hos.hpp" #include <stratosphere/hos.hpp>
#include "stratosphere/kvdb.hpp" #include <stratosphere/kvdb.hpp>
#include "stratosphere/ldr.hpp" #include <stratosphere/ldr.hpp>
#include "stratosphere/lr.hpp" #include <stratosphere/lr.hpp>
#include "stratosphere/map.hpp" #include <stratosphere/map.hpp>
#include "stratosphere/ncm.hpp" #include <stratosphere/ncm.hpp>
#include "stratosphere/nim.hpp" #include <stratosphere/nim.hpp>
#include "stratosphere/patcher.hpp" #include <stratosphere/patcher.hpp>
#include "stratosphere/pm.hpp" #include <stratosphere/psc.hpp>
#include "stratosphere/reg.hpp" #include <stratosphere/pm.hpp>
#include "stratosphere/ro.hpp" #include <stratosphere/reg.hpp>
#include "stratosphere/settings.hpp" #include <stratosphere/ro.hpp>
#include "stratosphere/sf.hpp" #include <stratosphere/settings.hpp>
#include "stratosphere/sm.hpp" #include <stratosphere/sf.hpp>
#include "stratosphere/spl.hpp" #include <stratosphere/sm.hpp>
#include "stratosphere/updater.hpp" #include <stratosphere/spl.hpp>
#include <stratosphere/time.hpp>
#include <stratosphere/updater.hpp>
/* Include FS last. */ /* Include FS last. */
#include "stratosphere/fs.hpp" #include <stratosphere/fs.hpp>
#include "stratosphere/fssrv.hpp" #include <stratosphere/fssrv.hpp>
#include "stratosphere/fssystem.hpp" #include <stratosphere/fssystem.hpp>

View file

@ -17,3 +17,9 @@
#pragma once #pragma once
#include <stratosphere/erpt/erpt_ids.autogen.hpp> #include <stratosphere/erpt/erpt_ids.autogen.hpp>
#include <stratosphere/erpt/erpt_types.hpp>
#include <stratosphere/erpt/erpt_multiple_category_context.hpp>
#include <stratosphere/erpt/sf/erpt_sf_i_context.hpp>
#include <stratosphere/erpt/sf/erpt_sf_i_session.hpp>
#include <stratosphere/erpt/srv/erpt_srv_types.hpp>
#include <stratosphere/erpt/srv/erpt_srv_api.hpp>

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/erpt/erpt_types.hpp>
#include <stratosphere/sf/sf_buffer_tags.hpp>
namespace ams::erpt {
constexpr inline u32 CategoriesPerMultipleCategoryContext = 0x10;
constexpr inline u32 FieldsPerMultipleCategoryContext = CategoriesPerMultipleCategoryContext * 4;
struct MultipleCategoryContextEntry : public sf::LargeData, public sf::PrefersMapAliasTransferMode {
u32 version;
u32 category_count;
CategoryId categories[CategoriesPerMultipleCategoryContext];
u32 field_counts[CategoriesPerMultipleCategoryContext];
u32 array_buf_counts[CategoriesPerMultipleCategoryContext];
FieldEntry fields[FieldsPerMultipleCategoryContext];
};
}

View file

@ -0,0 +1,234 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os.hpp>
#include <stratosphere/time/time_posix_time.hpp>
#include <stratosphere/erpt/erpt_ids.autogen.hpp>
namespace ams::erpt {
#define GENERATE_ENUM(NAME, ID, ...) NAME = ID,
enum FieldType {
AMS_ERPT_FOREACH_FIELD_TYPE(GENERATE_ENUM)
FieldType_Count,
};
#undef GENERATE_ENUM
#define GENERATE_ENUM(NAME, ID, ...) CategoryId_##NAME = ID,
enum CategoryId {
AMS_ERPT_FOREACH_CATEGORY(GENERATE_ENUM)
CategoryId_Count,
};
#undef GENERATE_ENUM
#define GENERATE_ENUM(NAME, ID, ...) FieldId_##NAME = ID,
enum FieldId {
AMS_ERPT_FOREACH_FIELD(GENERATE_ENUM)
FieldId_Count,
};
#undef GENERATE_ENUM
constexpr inline u32 ArrayBufferSizeDefault = 0x100;
constexpr inline u32 ArrayBufferSizeMax = 96_KB;
constexpr inline u32 ArrayFieldSizeMax = 16_KB - 1;
enum ReportType {
ReportType_Start = 0,
ReportType_Visible = ReportType_Start,
ReportType_Invisible = 1,
ReportType_End = 2,
ReportType_Count = ReportType_End,
ReportType_Any = ReportType_Count,
};
constexpr inline u32 ReportCountMax = 50;
constexpr inline u32 AttachmentsPerReportMax = 5;
constexpr inline u32 AttachmentCountMax = ReportCountMax * AttachmentsPerReportMax;
constexpr inline u32 ReportMetaDataSize = 0x20;
struct ReportMetaData {
u8 user_data[ReportMetaDataSize];
};
constexpr inline u32 ReportIdSize = 20;
struct ReportId {
union {
u8 id[ReportIdSize];
util::Uuid uuid;
#pragma pack(push, 1)
struct {
u32 time_low;
u16 time_mid;
u16 time_high_and_version;
u8 clock_high;
u8 clock_low;
u64 node;
} uuid_data;
#pragma pack(pop)
};
};
static_assert(sizeof(ReportId) == ReportIdSize);
inline bool operator==(const ReportId &lhs, const ReportId &rhs) {
return std::memcmp(lhs.id, rhs.id, sizeof(lhs.uuid)) == 0;
}
inline bool operator!=(const ReportId &lhs, const ReportId &rhs) {
return !(lhs == rhs);
}
struct ReportFlag {
using Transmitted = util::BitFlagSet<BITSIZEOF(u32), ReportFlag>::Flag<0>;
using HasAttachment = util::BitFlagSet<BITSIZEOF(u32), ReportFlag>::Flag<1>;
};
using ReportFlagSet = util::BitFlagSet<BITSIZEOF(u32), ReportFlag>;
static_assert(std::is_pod<ReportFlagSet>::value);
static_assert(sizeof(ReportFlagSet) == sizeof(u32));
struct ReportInfo {
ReportType type;
ReportId id;
ReportMetaData meta_data;
ReportFlagSet flags;
time::PosixTime timestamp_user;
time::PosixTime timestamp_network;
s64 report_size;
u64 reserved[3];
};
struct ReportList {
u32 report_count;
ReportInfo reports[ReportCountMax];
};
constexpr inline u32 AttachmentIdSize = 20;
struct AttachmentId {
union {
u8 id[AttachmentIdSize];
util::Uuid uuid;
};
};
static_assert(sizeof(AttachmentId) == AttachmentIdSize);
inline bool operator==(const AttachmentId &lhs, const AttachmentId &rhs) {
return std::memcmp(lhs.id, rhs.id, sizeof(lhs.uuid)) == 0;
}
inline bool operator!=(const AttachmentId &lhs, const AttachmentId &rhs) {
return !(lhs == rhs);
}
struct AttachmentFlag {
using HasOwner = util::BitFlagSet<BITSIZEOF(u32), AttachmentFlag>::Flag<1>;
};
using AttachmentFlagSet = util::BitFlagSet<BITSIZEOF(u32), AttachmentFlag>;
static_assert(std::is_pod<AttachmentFlagSet>::value);
static_assert(sizeof(AttachmentFlagSet) == sizeof(u32));
constexpr inline u32 AttachmentNameSizeMax = 0x20;
struct AttachmentInfo {
ReportId owner_report_id;
AttachmentId attachment_id;
AttachmentFlagSet flags;
s64 attachment_size;
char attachment_name[AttachmentNameSizeMax];
};
struct AttachmentList {
u32 attachment_count;
AttachmentInfo attachments[AttachmentsPerReportMax];
};
constexpr inline u32 AttachmentSizeMax = 512_KB;
struct FieldEntry {
FieldId id;
FieldType type;
union {
u64 value_u64;
u32 value_u32;
u16 value_u16;
u8 value_u8;
s64 value_i64;
s32 value_i32;
s16 value_i16;
s8 value_i8;
bool value_bool;
struct {
u32 start_idx;
u32 size;
} value_array;
};
};
constexpr inline u32 FieldsPerContext = 20;
struct ContextEntry {
u32 version;
u32 field_count;
CategoryId category;
FieldEntry fields[FieldsPerContext];
u8 *array_buffer;
u32 array_free_count;
u32 array_buffer_size;
};
struct StorageUsageStatistics {
util::Uuid journal_uuid;
u32 used_storage_size;
s64 max_report_size;
u32 report_count[ReportType_Count];
u32 transmitted_count[ReportType_Count];
u32 untransmitted_count[ReportType_Count];
};
/* https://github.com/msgpack/msgpack/blob/master/spec.md#overview */
enum class ValueTypeTag {
FixMap = 0x80,
FixArray = 0x90,
FixStr = 0xA0,
False = 0xC2,
True = 0xC3,
Bin8 = 0xC4,
Bin16 = 0xC5,
U8 = 0xCC,
U16 = 0xCD,
U32 = 0xCE,
U64 = 0xCF,
I8 = 0xD0,
I16 = 0xD1,
I32 = 0xD2,
I64 = 0xD3,
Str8 = 0xD9,
Str16 = 0xDA,
Array16 = 0xDC,
Map16 = 0xDE,
};
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/erpt/erpt_types.hpp>
namespace ams::erpt::sf {
class IAttachment : public ams::sf::IServiceObject {
protected:
enum class CommandId {
Open = 0,
Read = 1,
SetFlags = 2,
GetFlags = 3,
Close = 4,
GetSize = 5,
};
public:
/* Actual commands. */
virtual Result Open(const AttachmentId &attachment_id) = 0;
virtual Result Read(ams::sf::Out<u32> out_count, const ams::sf::OutBuffer &out_buffer) = 0;
virtual Result SetFlags(AttachmentFlagSet flags) = 0;
virtual Result GetFlags(ams::sf::Out<AttachmentFlagSet> out) = 0;
virtual Result Close() = 0;
virtual Result GetSize(ams::sf::Out<s64> out) = 0;
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(Open),
MAKE_SERVICE_COMMAND_META(Read),
MAKE_SERVICE_COMMAND_META(SetFlags),
MAKE_SERVICE_COMMAND_META(GetFlags),
MAKE_SERVICE_COMMAND_META(Close),
MAKE_SERVICE_COMMAND_META(GetSize),
};
};
}

View file

@ -0,0 +1,69 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/sf.hpp>
#include <stratosphere/erpt/erpt_types.hpp>
#include <stratosphere/erpt/erpt_multiple_category_context.hpp>
#include <stratosphere/time/time_steady_clock_time_point.hpp>
namespace ams::erpt::sf {
class IContext : public ams::sf::IServiceObject {
protected:
enum class CommandId {
SubmitContext = 0,
CreateReport = 1,
SetInitialLaunchSettingsCompletionTime = 2,
ClearInitialLaunchSettingsCompletionTime = 3,
UpdatePowerOnTime = 4,
UpdateAwakeTime = 5,
SubmitMultipleCategoryContext = 6,
UpdateApplicationLaunchTime = 7,
ClearApplicationLaunchTime = 8,
SubmitAttachment = 9,
CreateReportWithAttachments = 10,
};
public:
/* Actual commands. */
virtual Result SubmitContext(const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer) = 0;
virtual Result CreateReport(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &meta_buffer) = 0;
virtual Result SetInitialLaunchSettingsCompletionTime(const time::SteadyClockTimePoint &time_point) = 0;
virtual Result ClearInitialLaunchSettingsCompletionTime() = 0;
virtual Result UpdatePowerOnTime() = 0;
virtual Result UpdateAwakeTime() = 0;
virtual Result SubmitMultipleCategoryContext(const MultipleCategoryContextEntry &ctx_entry, const ams::sf::InBuffer &str_buffer) = 0;
virtual Result UpdateApplicationLaunchTime() = 0;
virtual Result ClearApplicationLaunchTime() = 0;
virtual Result SubmitAttachment(ams::sf::Out<AttachmentId> out, const ams::sf::InBuffer &attachment_name, const ams::sf::InBuffer &attachment_data) = 0;
virtual Result CreateReportWithAttachments(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &attachment_ids_buffer) = 0;
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(SubmitContext),
MAKE_SERVICE_COMMAND_META(CreateReport),
MAKE_SERVICE_COMMAND_META(SetInitialLaunchSettingsCompletionTime, hos::Version_300),
MAKE_SERVICE_COMMAND_META(ClearInitialLaunchSettingsCompletionTime, hos::Version_300),
MAKE_SERVICE_COMMAND_META(UpdatePowerOnTime, hos::Version_300),
MAKE_SERVICE_COMMAND_META(UpdateAwakeTime, hos::Version_300),
MAKE_SERVICE_COMMAND_META(SubmitMultipleCategoryContext, hos::Version_500),
MAKE_SERVICE_COMMAND_META(UpdateApplicationLaunchTime, hos::Version_600),
MAKE_SERVICE_COMMAND_META(ClearApplicationLaunchTime, hos::Version_600),
MAKE_SERVICE_COMMAND_META(SubmitAttachment, hos::Version_800),
MAKE_SERVICE_COMMAND_META(CreateReportWithAttachments, hos::Version_800),
};
};
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/erpt/erpt_types.hpp>
namespace ams::erpt::sf {
class IManager : public ams::sf::IServiceObject {
protected:
enum class CommandId {
GetReportList = 0,
GetEvent = 1,
CleanupReports = 2,
DeleteReport = 3,
GetStorageUsageStatistics = 4,
GetAttachmentList = 5,
};
public:
/* Actual commands. */
virtual Result GetReportList(const ams::sf::OutBuffer &out_list, ReportType type_filter) = 0;
virtual Result GetEvent(ams::sf::OutCopyHandle out) = 0;
virtual Result CleanupReports() = 0;
virtual Result DeleteReport(const ReportId &report_id) = 0;
virtual Result GetStorageUsageStatistics(ams::sf::Out<StorageUsageStatistics> out) = 0;
virtual Result GetAttachmentList(const ams::sf::OutBuffer &out_buf, const ReportId &report_id) = 0;
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(GetReportList),
MAKE_SERVICE_COMMAND_META(GetEvent),
MAKE_SERVICE_COMMAND_META(CleanupReports, hos::Version_400),
MAKE_SERVICE_COMMAND_META(DeleteReport, hos::Version_500),
MAKE_SERVICE_COMMAND_META(GetStorageUsageStatistics, hos::Version_500),
MAKE_SERVICE_COMMAND_META(GetAttachmentList, hos::Version_800),
};
};
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/erpt/erpt_types.hpp>
namespace ams::erpt::sf {
class IReport : public ams::sf::IServiceObject {
protected:
enum class CommandId {
Open = 0,
Read = 1,
SetFlags = 2,
GetFlags = 3,
Close = 4,
GetSize = 5,
};
public:
/* Actual commands. */
virtual Result Open(const ReportId &report_id) = 0;
virtual Result Read(ams::sf::Out<u32> out_count, const ams::sf::OutBuffer &out_buffer) = 0;
virtual Result SetFlags(ReportFlagSet flags) = 0;
virtual Result GetFlags(ams::sf::Out<ReportFlagSet> out) = 0;
virtual Result Close() = 0;
virtual Result GetSize(ams::sf::Out<s64> out) = 0;
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(Open),
MAKE_SERVICE_COMMAND_META(Read),
MAKE_SERVICE_COMMAND_META(SetFlags),
MAKE_SERVICE_COMMAND_META(GetFlags),
MAKE_SERVICE_COMMAND_META(Close),
MAKE_SERVICE_COMMAND_META(GetSize),
};
};
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/erpt/erpt_types.hpp>
#include <stratosphere/erpt/sf/erpt_sf_i_report.hpp>
#include <stratosphere/erpt/sf/erpt_sf_i_manager.hpp>
#include <stratosphere/erpt/sf/erpt_sf_i_attachment.hpp>
namespace ams::erpt::sf {
class ISession : public ams::sf::IServiceObject {
protected:
enum class CommandId {
OpenReport = 0,
OpenManager = 1,
OpenAttachment = 2,
};
public:
/* Actual commands. */
virtual Result OpenReport(ams::sf::Out<std::shared_ptr<erpt::sf::IReport>> out) = 0;
virtual Result OpenManager(ams::sf::Out<std::shared_ptr<erpt::sf::IManager>> out) = 0;
virtual Result OpenAttachment(ams::sf::Out<std::shared_ptr<erpt::sf::IAttachment>> out) = 0;
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(OpenReport),
MAKE_SERVICE_COMMAND_META(OpenManager),
MAKE_SERVICE_COMMAND_META(OpenAttachment, hos::Version_800),
};
};
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::erpt::srv {
Result Initialize(u8 *mem, size_t mem_size);
Result InitializeAndStartService();
Result SetSerialNumberAndOsVersion(const char *sn, u32 sn_len, const char *os, u32 os_len, const char *os_priv, u32 os_priv_len);
Result SetProductModel(const char *model, u32 model_len);
Result SetRegionSetting(const char *region, u32 region_len);
/* Atmosphere extension. */
Result SetRedirectNewReportsToSdCard(bool redirect);
void Wait();
}

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os.hpp>
#include <stratosphere/erpt/erpt_ids.autogen.hpp>
namespace ams::erpt::srv {
constexpr inline const char ReportOnSdStoragePath[] = "ersd";
constexpr inline const char ReportStoragePath[] = "save";
constexpr inline const char JournalFileName[] = "save:/journal";
constexpr size_t ReportFileNameLength = 64;
constexpr size_t AttachmentFileNameLength = 64;
constexpr size_t MaxFieldStringSize = 64;
struct ReportFileName {
char name[ReportFileNameLength];
};
struct AttachmentFileName {
char name[AttachmentFileNameLength];
};
enum FieldFlag : u8 {
FieldFlag_None = 0,
FieldFlag_Encrypt = 1,
};
#define STRINGIZE_HANDLER(NAME, ...) #NAME,
constexpr inline const char * const FieldString[] = {
AMS_ERPT_FOREACH_FIELD(STRINGIZE_HANDLER)
};
constexpr inline const char * const CategoryString[] = {
AMS_ERPT_FOREACH_CATEGORY(STRINGIZE_HANDLER)
};
constexpr inline const char * const TypeString[] = {
AMS_ERPT_FOREACH_FIELD_TYPE(STRINGIZE_HANDLER)
};
#undef STRINGIZE_HANDLER
#define GET_FIELD_CATEGORY(FIELD, ID, CATEGORY, TYPE, FLAG) [FieldId_##FIELD] = CategoryId_##CATEGORY,
constexpr inline const CategoryId FieldToCategoryMap[] = {
AMS_ERPT_FOREACH_FIELD(GET_FIELD_CATEGORY)
};
#undef GET_FIELD_CATEGORY
#define GET_FIELD_TYPE(FIELD, ID, CATEGORY, TYPE, FLAG) [FieldId_##FIELD] = TYPE,
constexpr inline const FieldType FieldToTypeMap[] = {
AMS_ERPT_FOREACH_FIELD(GET_FIELD_TYPE)
};
#undef GET_FIELD_TYPE
#define GET_FIELD_FLAG(FIELD, ID, CATEGORY, TYPE, FLAG) [FieldId_##FIELD] = FLAG,
constexpr inline const FieldFlag FieldToFlagMap[] = {
AMS_ERPT_FOREACH_FIELD(GET_FIELD_FLAG)
};
#undef GET_FIELD_FLAG
constexpr inline ReportFlagSet MakeNoReportFlags() {
return util::MakeBitFlagSet<32, ReportFlag>();
}
constexpr inline AttachmentFlagSet MakeNoAttachmentFlags() {
return util::MakeBitFlagSet<32, AttachmentFlag>();
}
}

View file

@ -26,4 +26,9 @@ namespace ams::fs {
Result GetSaveDataFlags(u32 *out, SaveDataSpaceId space_id, SaveDataId id); Result GetSaveDataFlags(u32 *out, SaveDataSpaceId space_id, SaveDataId id);
Result SetSaveDataFlags(SaveDataId id, SaveDataSpaceId space_id, u32 flags); Result SetSaveDataFlags(SaveDataId id, SaveDataSpaceId space_id, u32 flags);
Result GetSaveDataAvailableSize(s64 *out, SaveDataId id);
Result GetSaveDataJournalSize(s64 *out, SaveDataId id);
Result ExtendSaveData(SaveDataSpaceId space_id, SaveDataId id, s64 available_size, s64 journal_size);
} }

View file

@ -20,4 +20,6 @@ namespace ams::fs {
Result MountSdCard(const char *name); Result MountSdCard(const char *name);
Result MountSdCardErrorReportDirectoryForAtmosphere(const char *name);
} }

View file

@ -14,15 +14,16 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "../../fs/fs_common.hpp" #include <stratosphere/fs/fs_common.hpp>
#include "../../fs/fsa/fs_ifile.hpp" #include <stratosphere/fs/fsa/fs_ifile.hpp>
#include "../../fs/fsa/fs_idirectory.hpp" #include <stratosphere/fs/fsa/fs_idirectory.hpp>
#include "../../fs/fsa/fs_ifilesystem.hpp" #include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
#include <stratosphere/fs/impl/fs_newable.hpp>
namespace ams::fssystem::impl { namespace ams::fssystem::impl {
template<typename Impl> template<typename Impl>
class IPathResolutionFileSystem : public fs::fsa::IFileSystem { class IPathResolutionFileSystem : public fs::fsa::IFileSystem, public fs::impl::Newable {
NON_COPYABLE(IPathResolutionFileSystem); NON_COPYABLE(IPathResolutionFileSystem);
private: private:
std::shared_ptr<fs::fsa::IFileSystem> shared_fs; std::shared_ptr<fs::fsa::IFileSystem> shared_fs;

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2020 Adubbz, Atmosphère-NX * Copyright (c) 2019-2020 Atmosphère-NX
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License, * under the terms and conditions of the GNU General Public License,

View file

@ -0,0 +1,22 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/psc/psc_types.hpp>
#include <stratosphere/psc/psc_pm_module_id.hpp>
#include <stratosphere/psc/sf/psc_sf_i_pm_module.hpp>
#include <stratosphere/psc/psc_pm_module.hpp>

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#if defined(ATMOSPHERE_OS_HORIZON)
#include <stratosphere/psc/psc_pm_module.os.horizon.hpp>
#else
#error "Unknown OS for psc::PmModule"
#endif

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/psc/psc_types.hpp>
#include <stratosphere/psc/psc_pm_module_id.hpp>
#include <stratosphere/psc/sf/psc_sf_i_pm_module.hpp>
namespace ams::psc {
class PmModule {
NON_COPYABLE(PmModule);
NON_MOVEABLE(PmModule);
private:
std::shared_ptr<psc::sf::IPmModule> intf;
os::SystemEvent system_event;
bool initialized;
PmModuleId module_id;
uintptr_t reserved;
public:
PmModule();
~PmModule();
Result Initialize(const PmModuleId mid, const PmModuleId *dependencies, u32 dependency_count, os::EventClearMode clear_mode);
Result Finalize();
constexpr PmModuleId GetId() const { return this->module_id; }
Result GetRequest(PmState *out_state, PmFlagSet *out_flags);
Result Acknowledge(PmState state, Result res);
os::SystemEvent *GetEventPointer();
};
}

View file

@ -0,0 +1,83 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::psc {
enum PmModuleId : u16 {
PmModuleId_Usb = 4,
PmModuleId_Ethernet = 5,
PmModuleId_Fgm = 6,
PmModuleId_PcvClock = 7,
PmModuleId_PcvVoltage = 8,
PmModuleId_Gpio = 9,
PmModuleId_Pinmux = 10,
PmModuleId_Uart = 11,
PmModuleId_I2c = 12,
PmModuleId_I2cPcv = 13,
PmModuleId_Spi = 14,
PmModuleId_Pwm = 15,
PmModuleId_Psm = 16,
PmModuleId_Tc = 17,
PmModuleId_Omm = 18,
PmModuleId_Pcie = 19,
PmModuleId_Lbl = 20,
PmModuleId_Display = 21,
PmModuleId_Hid = 24,
PmModuleId_WlanSockets = 25,
PmModuleId_Fs = 27,
PmModuleId_Audio = 28,
PmModuleId_TmaHostIo = 30,
PmModuleId_Bluetooth = 31,
PmModuleId_Bpc = 32,
PmModuleId_Fan = 33,
PmModuleId_Pcm = 34,
PmModuleId_Nfc = 35,
PmModuleId_Apm = 36,
PmModuleId_Btm = 37,
PmModuleId_Nifm = 38,
PmModuleId_GpioLow = 39,
PmModuleId_Npns = 40,
PmModuleId_Lm = 41,
PmModuleId_Bcat = 42,
PmModuleId_Time = 43,
PmModuleId_Pctl = 44,
PmModuleId_Erpt = 45,
PmModuleId_Eupld = 46,
PmModuleId_Friends = 47,
PmModuleId_Bgtc = 48,
PmModuleId_Account = 49,
PmModuleId_Sasbus = 50,
PmModuleId_Ntc = 51,
PmModuleId_Idle = 52,
PmModuleId_Tcap = 53,
PmModuleId_PsmLow = 54,
PmModuleId_Ndd = 55,
PmModuleId_Olsc = 56,
PmModuleId_Ns = 61,
PmModuleId_Nvservices = 101,
PmModuleId_Spsm = 127,
};
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::psc {
enum PmState {
PmState_Awake = 0,
PmState_ReadyAwaken = 1,
PmState_ReadySleep = 2,
PmState_ReadySleepCritical = 3,
PmState_ReadyAwakenCritical = 4,
PmState_ReadyShutdown = 5,
};
constexpr inline u32 MaximumDependencyLevels = 20;
struct PmFlag {
};
using PmFlagSet = util::BitFlagSet<BITSIZEOF(u32), PmFlag>;
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/psc/psc_types.hpp>
#include <stratosphere/psc/psc_pm_module_id.hpp>
namespace ams::psc::sf {
class IPmModule : public ams::sf::IServiceObject {
protected:
enum class CommandId {
Initialize = 0,
GetRequest = 1,
Acknowledge = 2,
Finalize = 3,
AcknowledgeEx = 4,
};
public:
/* Actual commands. */
virtual Result Initialize(ams::sf::OutCopyHandle out, psc::PmModuleId module_id, const ams::sf::InBuffer &child_list) = 0;
virtual Result GetRequest(ams::sf::Out<PmState> out_state, ams::sf::Out<PmFlagSet> out_flags) = 0;
virtual Result Acknowledge() = 0;
virtual Result Finalize() = 0;
virtual Result AcknowledgeEx(PmState state) = 0;
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(Initialize),
MAKE_SERVICE_COMMAND_META(GetRequest),
MAKE_SERVICE_COMMAND_META(Acknowledge),
MAKE_SERVICE_COMMAND_META(Finalize),
MAKE_SERVICE_COMMAND_META(AcknowledgeEx, hos::Version_600), /* TODO: This is really 5.1.0... */
};
};
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/psc/sf/psc_sf_i_pm_module.hpp>
namespace ams::psc::sf {
class IPmService : public ams::sf::IServiceObject {
protected:
enum class CommandId {
Initialize = 0,
};
public:
/* Actual commands. */
virtual Result Initialize(ams::sf::Out<std::shared_ptr<psc::sf::IPmModule>> out) = 0;
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(Initialize),
};
};
}

View file

@ -19,3 +19,7 @@
#include "settings/settings_types.hpp" #include "settings/settings_types.hpp"
#include "settings/settings_fwdbg_types.hpp" #include "settings/settings_fwdbg_types.hpp"
#include "settings/settings_fwdbg_api.hpp" #include "settings/settings_fwdbg_api.hpp"
#include "settings/system/settings_firmware_version.hpp"
#include "settings/system/settings_product_model.hpp"
#include "settings/system/settings_region.hpp"
#include "settings/system/settings_serial_number.hpp"

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/settings/settings_types.hpp>
namespace ams::settings::system {
struct alignas(4) FirmwareVersion {
u8 major;
u8 minor;
u8 micro;
u8 padding1;
u8 revision_major;
u8 revision_minor;
u8 padding2;
u8 padding3;
char platform[0x20];
char revision[0x40];
char display_version[0x18];
char display_name[0x80];
constexpr int GetComparableVersion() const {
return (static_cast<int>(major) << 16) | (static_cast<int>(minor) << 8) | (static_cast<int>(micro) << 0);
}
constexpr friend bool operator==(const FirmwareVersion &lhs, const FirmwareVersion &rhs) {
return lhs.GetComparableVersion() == rhs.GetComparableVersion();
}
constexpr friend bool operator!=(const FirmwareVersion &lhs, const FirmwareVersion &rhs) {
return lhs.GetComparableVersion() != rhs.GetComparableVersion();
}
constexpr friend bool operator<=(const FirmwareVersion &lhs, const FirmwareVersion &rhs) {
return lhs.GetComparableVersion() <= rhs.GetComparableVersion();
}
constexpr friend bool operator>=(const FirmwareVersion &lhs, const FirmwareVersion &rhs) {
return lhs.GetComparableVersion() >= rhs.GetComparableVersion();
}
constexpr friend bool operator<(const FirmwareVersion &lhs, const FirmwareVersion &rhs) {
return lhs.GetComparableVersion() < rhs.GetComparableVersion();
}
constexpr friend bool operator>(const FirmwareVersion &lhs, const FirmwareVersion &rhs) {
return lhs.GetComparableVersion() > rhs.GetComparableVersion();
}
};
void GetFirmwareVersion(FirmwareVersion *out);
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/settings/settings_types.hpp>
namespace ams::settings::system {
enum ProductModel {
ProductModel_Invalid = 0,
ProductModel_Nx = 1,
};
ProductModel GetProductModel();
}

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/settings/settings_types.hpp>
namespace ams::settings::system {
enum RegionCode {
RegionCode_Japan = 0,
RegionCode_Usa = 1,
RegionCode_Europe = 2,
RegionCode_Australia = 3,
RegionCode_HongKongTaiwanKorea = 4,
RegionCode_China = 5,
};
void GetRegionCode(RegionCode *out);
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/settings/settings_types.hpp>
namespace ams::settings::system {
struct SerialNumber {
char str[0x18];
};
void GetSerialNumber(SerialNumber *out);
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/time/time_common.hpp>
#include <stratosphere/time/time_posix_time.hpp>
#include <stratosphere/time/time_steady_clock_time_point.hpp>
#include <stratosphere/time/time_api.hpp>
#include <stratosphere/time/time_standard_steady_clock.hpp>
#include <stratosphere/time/time_standard_user_system_clock.hpp>
#include <stratosphere/time/time_standard_network_system_clock.hpp>
#include <stratosphere/time/impl/util/time_impl_util_api.hpp>

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/time/time_common.hpp>
#include <stratosphere/time/time_posix_time.hpp>
#include <stratosphere/time/time_steady_clock_time_point.hpp>
namespace ams::time::impl::util {
Result GetSpanBetween(s64 *out, const SteadyClockTimePoint &from, const SteadyClockTimePoint &to);
}

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/time/time_common.hpp>
#include <stratosphere/time/time_posix_time.hpp>
#include <stratosphere/time/time_steady_clock_time_point.hpp>
namespace ams::time {
Result Initialize();
Result InitializeForSystem();
Result InitializeForSystemUser();
Result Finalize();
bool IsInitialized();
Result GetElapsedSecondsBetween(s64 *out, const SteadyClockTimePoint &from, const SteadyClockTimePoint &to);
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::time {
using SourceId = util::Uuid;
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/time/time_common.hpp>
namespace ams::time {
struct PosixTime {
s64 value;
constexpr PosixTime &operator+=(const TimeSpan &ts) { this->value += ts.GetSeconds(); return *this; }
constexpr PosixTime &operator-=(const TimeSpan &ts) { this->value -= ts.GetSeconds(); return *this; }
constexpr friend PosixTime operator+(const PosixTime &lhs, const TimeSpan &rhs) { return { .value = lhs.value + rhs.GetSeconds() }; }
constexpr friend PosixTime operator-(const PosixTime &lhs, const TimeSpan &rhs) { return { .value = lhs.value - rhs.GetSeconds() }; }
constexpr friend TimeSpan operator-(const PosixTime &lhs, const PosixTime &rhs) { return TimeSpan::FromSeconds(lhs.value - rhs.value); }
constexpr friend bool operator==(const PosixTime &lhs, const PosixTime &rhs) { return lhs.value == rhs.value; }
constexpr friend bool operator!=(const PosixTime &lhs, const PosixTime &rhs) { return lhs.value != rhs.value; }
constexpr friend bool operator<=(const PosixTime &lhs, const PosixTime &rhs) { return lhs.value <= rhs.value; }
constexpr friend bool operator>=(const PosixTime &lhs, const PosixTime &rhs) { return lhs.value >= rhs.value; }
constexpr friend bool operator< (const PosixTime &lhs, const PosixTime &rhs) { return lhs.value < rhs.value; }
constexpr friend bool operator> (const PosixTime &lhs, const PosixTime &rhs) { return lhs.value > rhs.value; }
};
static_assert(sizeof(PosixTime) == sizeof(s64));
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/time/time_common.hpp>
#include <stratosphere/time/time_system_clock_common.hpp>
#include <stratosphere/time/time_posix_time.hpp>
namespace ams::time {
class StandardNetworkSystemClock {
public:
using rep = SystemClockTraits::rep;
using period = SystemClockTraits::period;
using duration = SystemClockTraits::duration;
using time_point = SystemClockTraits::time_point;
static constexpr bool is_steady = false;
public:
static time_point now();
static std::time_t to_time_t(const StandardUserSystemClock::time_point &t);
static time_point from_time_t(std::time_t t);
public:
static Result GetCurrentTime(PosixTime *out);
/* TODO: static Result GetSystemClockContext(SystemClockContext *out); */
};
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/time/time_common.hpp>
#include <stratosphere/time/time_steady_clock_time_point.hpp>
namespace ams::time {
class StandardSteadyClock {
public:
using rep = s64;
using period = std::ratio<1>;
using duration = std::chrono::duration<rep, period>;
using time_point = std::chrono::time_point<StandardSteadyClock>;
static constexpr bool is_steady = true;
public:
static time_point now();
public:
static Result GetCurrentTimePoint(SteadyClockTimePoint *out);
};
Result GetStandardSteadyClockCurrentTimePoint(SteadyClockTimePoint *out);
TimeSpan GetStandardSteadyClockInternalOffset();
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/time/time_common.hpp>
#include <stratosphere/time/time_system_clock_common.hpp>
#include <stratosphere/time/time_posix_time.hpp>
namespace ams::time {
class StandardUserSystemClock {
public:
using rep = SystemClockTraits::rep;
using period = SystemClockTraits::period;
using duration = SystemClockTraits::duration;
using time_point = SystemClockTraits::time_point;
static constexpr bool is_steady = false;
public:
static time_point now();
static std::time_t to_time_t(const StandardUserSystemClock::time_point &t);
static time_point from_time_t(std::time_t t);
public:
static Result GetCurrentTime(PosixTime *out);
/* TODO: static Result GetSystemClockContext(SystemClockContext *out); */
};
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/time/time_common.hpp>
namespace ams::time {
struct SteadyClockTimePoint {
s64 value;
SourceId source_id;
constexpr SteadyClockTimePoint &operator+=(const TimeSpan &ts) { this->value += ts.GetSeconds(); return *this; }
constexpr SteadyClockTimePoint &operator-=(const TimeSpan &ts) { this->value -= ts.GetSeconds(); return *this; }
constexpr friend SteadyClockTimePoint operator+(const SteadyClockTimePoint &lhs, const TimeSpan &rhs) { return { .value = lhs.value + rhs.GetSeconds(), .source_id = lhs.source_id }; }
constexpr friend SteadyClockTimePoint operator-(const SteadyClockTimePoint &lhs, const TimeSpan &rhs) { return { .value = lhs.value - rhs.GetSeconds(), .source_id = lhs.source_id }; }
constexpr friend bool operator==(const SteadyClockTimePoint &lhs, const SteadyClockTimePoint &rhs) { return lhs.value == rhs.value && lhs.source_id == rhs.source_id; }
constexpr friend bool operator!=(const SteadyClockTimePoint &lhs, const SteadyClockTimePoint &rhs) { return !(lhs == rhs); }
};
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/time/time_common.hpp>
namespace ams::time {
class SystemClockTraits {
public:
using rep = s64;
using period = std::ratio<1>;
using duration = std::chrono::duration<rep, period>;
using time_point = std::chrono::time_point<SystemClockTraits>;
};
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::crypto {
namespace {
bool g_initialized;
os::Mutex g_lock(false);
void InitializeCsrng() {
AMS_ASSERT(!g_initialized);
sm::DoWithSession([&]() {
R_ABORT_UNLESS(::csrngInitialize());
});
}
}
void GenerateCryptographicallyRandomBytes(void *dst, size_t dst_size) {
{
std::scoped_lock lk(g_lock);
if (AMS_UNLIKELY(!g_initialized)) {
InitializeCsrng();
g_initialized = true;
}
}
R_ABORT_UNLESS(csrngGetRandomBytes(dst, dst_size));
}
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::erpt::srv {
extern lmem::HeapHandle g_heap_handle;
class Allocator {
public:
void *operator new(size_t sz) { return lmem::AllocateFromExpHeap(g_heap_handle, sz); }
void *operator new(size_t sz, size_t algn) { return lmem::AllocateFromExpHeap(g_heap_handle, sz, static_cast<s32>(algn)); }
void *operator new[](size_t sz) { return lmem::AllocateFromExpHeap(g_heap_handle, sz); }
void *operator new[](size_t sz, size_t algn) { return lmem::AllocateFromExpHeap(g_heap_handle, sz, static_cast<s32>(algn)); }
void operator delete(void *p) { lmem::FreeToExpHeap(g_heap_handle, p); }
void operator delete[](void *p) { 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) {
return lmem::FreeToExpHeap(g_heap_handle, p);
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "erpt_srv_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;
std::snprintf(attachment_name.name, sizeof(attachment_name.name), "%s/%s.att", ReportStoragePath, uuid_str);
return attachment_name;
}
Attachment::Attachment(JournalRecord<AttachmentInfo> *r) : record(r) {
this->record->AddReference();
}
Attachment::~Attachment() {
this->CloseStream();
if (this->record->RemoveReference()) {
this->DeleteStream(this->FileName().name);
delete this->record;
}
}
AttachmentFileName Attachment::FileName() {
return FileName(this->record->info.attachment_id);
}
Result Attachment::Open(AttachmentOpenType type) {
switch (type) {
case AttachmentOpenType_Create: return this->OpenStream(this->FileName().name, StreamMode_Write, AttachmentStreamBufferSize);
case AttachmentOpenType_Read: return this->OpenStream(this->FileName().name, StreamMode_Read, AttachmentStreamBufferSize);
default: return erpt::ResultInvalidArgument();
}
}
Result Attachment::Read(u32 *out_read_count, u8 *dst, u32 dst_size) {
return this->ReadStream(out_read_count, dst, dst_size);
}
Result Attachment::Delete() {
return this->DeleteStream(this->FileName().name);
}
void Attachment::Close() {
return this->CloseStream();
}
Result Attachment::GetFlags(AttachmentFlagSet *out) {
*out = this->record->info.flags;
return ResultSuccess();
}
Result Attachment::SetFlags(AttachmentFlagSet flags) {
if (((~this->record->info.flags) & flags).IsAnySet()) {
this->record->info.flags |= flags;
return Journal::Commit();
}
return ResultSuccess();
}
Result Attachment::GetSize(s64 *out) {
return this->GetStreamSize(out);
}
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#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> *record;
private:
AttachmentFileName FileName();
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);
Result SetFlags(AttachmentFlagSet flags);
Result GetSize(s64 *out);
template<typename T>
Result Write(T val) {
return this->WriteStream(reinterpret_cast<const u8 *>(std::addressof(val)), sizeof(val));
}
template<typename T>
Result Write(const T *buf, u32 buffer_size) {
return this->WriteStream(reinterpret_cast<const u8 *>(buf), buffer_size);
}
};
}

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "erpt_srv_attachment_impl.hpp"
#include "erpt_srv_attachment.hpp"
namespace ams::erpt::srv {
AttachmentImpl::AttachmentImpl() : attachment(nullptr) {
/* ... */
}
AttachmentImpl::~AttachmentImpl() {
R_ABORT_UNLESS(this->Close());
}
Result AttachmentImpl::Open(const AttachmentId &attachment_id) {
R_UNLESS(this->attachment == nullptr, erpt::ResultAlreadyInitialized());
JournalRecord<AttachmentInfo> *record = Journal::Retrieve(attachment_id);
R_UNLESS(record != nullptr, erpt::ResultNotFound());
this->attachment = new Attachment(record);
R_UNLESS(this->attachment != nullptr, erpt::ResultOutOfMemory());
auto attachment_guard = SCOPE_GUARD { delete this->attachment; this->attachment = nullptr; };
R_TRY(this->attachment->Open(AttachmentOpenType_Read));
attachment_guard.Cancel();
return ResultSuccess();
}
Result AttachmentImpl::Read(ams::sf::Out<u32> out_count, const ams::sf::OutBuffer &out_buffer) {
R_UNLESS(this->attachment != nullptr, erpt::ResultNotInitialized());
return this->attachment->Read(out_count.GetPointer(), static_cast<u8 *>(out_buffer.GetPointer()), static_cast<u32>(out_buffer.GetSize()));
}
Result AttachmentImpl::SetFlags(AttachmentFlagSet flags) {
R_UNLESS(this->attachment != nullptr, erpt::ResultNotInitialized());
return this->attachment->SetFlags(flags);
}
Result AttachmentImpl::GetFlags(ams::sf::Out<AttachmentFlagSet> out) {
R_UNLESS(this->attachment != nullptr, erpt::ResultNotInitialized());
return this->attachment->GetFlags(out.GetPointer());
}
Result AttachmentImpl::Close() {
if (this->attachment != nullptr) {
this->attachment->Close();
delete this->attachment;
this->attachment = nullptr;
}
return ResultSuccess();
}
Result AttachmentImpl::GetSize(ams::sf::Out<s64> out) {
R_UNLESS(this->attachment != nullptr, erpt::ResultNotInitialized());
return this->attachment->GetSize(out.GetPointer());
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::erpt::srv {
class Attachment;
class AttachmentImpl final : public erpt::sf::IAttachment {
private:
Attachment *attachment;
public:
AttachmentImpl();
~AttachmentImpl();
public:
virtual Result Open(const AttachmentId &attachment_id) override final;
virtual Result Read(ams::sf::Out<u32> out_count, const ams::sf::OutBuffer &out_buffer) override final;
virtual Result SetFlags(AttachmentFlagSet flags) override final;
virtual Result GetFlags(ams::sf::Out<AttachmentFlagSet> out) override final;
virtual Result Close() override final;
virtual Result GetSize(ams::sf::Out<s64> out) override final;
};
}

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "erpt_srv_cipher.hpp"
namespace ams::erpt::srv {
u8 Cipher::s_key[crypto::Aes128CtrEncryptor::KeySize + crypto::Aes128CtrEncryptor::IvSize + crypto::Aes128CtrEncryptor::BlockSize];
bool Cipher::s_need_to_store_cipher = false;
}

View file

@ -0,0 +1,125 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#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>(FieldToTypeMap[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; };
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));
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));
return Formatter::End(report);
}
static Result AddField(Report *report, FieldId field_id, bool value) {
return Formatter::AddField(report, field_id, value);
}
template<typename T>
static Result AddField(Report *report, FieldId field_id, T value) {
return Formatter::AddField<T>(report, field_id, value);
}
static Result AddField(Report *report, FieldId field_id, char *str, u32 len) {
if (FieldToFlagMap[field_id] == FieldFlag_Encrypt) {
return EncryptArray<char>(report, field_id, str, len);
} else {
return Formatter::AddField(report, field_id, str, len);
}
}
static Result AddField(Report *report, FieldId field_id, u8 *bin, u32 len) {
if (FieldToFlagMap[field_id] == FieldFlag_Encrypt) {
return EncryptArray<u8>(report, field_id, bin, len);
} else {
return Formatter::AddField(report, field_id, bin, len);
}
}
template<typename T>
static Result AddField(Report *report, FieldId field_id, T *arr, u32 len) {
if (FieldToFlagMap[field_id] == FieldFlag_Encrypt) {
return EncryptArray<T>(report, field_id, arr, len);
} else {
return Formatter::AddField<T>(report, field_id, arr, len);
}
}
};
}

View file

@ -0,0 +1,131 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "erpt_srv_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;
ContextList g_category_list;
}
Context::Context(CategoryId cat, u32 max_records) : category(cat), max_record_count(max_records), record_count(0) {
g_category_list.push_front(*this);
}
Context::~Context() {
g_category_list.erase(g_category_list.iterator_to(*this));
}
Result Context::AddCategoryToReport(Report *report) {
R_SUCCEED_IF(this->record_list.empty());
for (auto it = this->record_list.begin(); it != this->record_list.end(); it++) {
for (u32 i = 0; i < it->ctx.field_count; i++) {
auto *field = std::addressof(it->ctx.fields[i]);
u8 *arr_buf = it->ctx.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: return erpt::ResultInvalidArgument();
}
}
}
return ResultSuccess();
}
Result Context::AddContextToCategory(const ContextEntry *entry, const u8 *data, u32 data_size) {
ContextRecord *record = new ContextRecord();
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
auto guard = SCOPE_GUARD { delete record; };
R_TRY(record->Initialize(entry, data, data_size));
guard.Cancel();
this->AddContextRecordToCategory(record);
return ResultSuccess();
}
Result Context::AddContextRecordToCategory(ContextRecord *record) {
if (this->record_count < this->max_record_count) {
this->record_list.push_front(*record);
this->record_count++;
} else {
ContextRecord *back = std::addressof(this->record_list.back());
this->record_list.pop_back();
this->record_list.push_front(*record);
delete back;
}
return ResultSuccess();
}
Result Context::SubmitContext(const ContextEntry *entry, const u8 *data, u32 data_size) {
for (auto it = g_category_list.begin(); it != g_category_list.end(); it++) {
if (it->category == entry->category) {
return it->AddContextToCategory(entry, data, data_size);
}
}
return erpt::ResultCategoryNotFound();
}
Result Context::SubmitContextRecord(ContextRecord *record) {
for (auto it = g_category_list.begin(); it != g_category_list.end(); it++) {
if (it->category == record->ctx.category) {
return it->AddContextRecordToCategory(record);
}
}
return erpt::ResultCategoryNotFound();
}
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();
return ResultSuccess();
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#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 category;
const u32 max_record_count;
u32 record_count;
util::IntrusiveListBaseTraits<ContextRecord>::ListType record_list;
public:
Context(CategoryId cat, u32 max_records);
~Context();
Result AddCategoryToReport(Report *report);
Result AddContextToCategory(const ContextEntry *entry, const u8 *data, u32 data_size);
Result AddContextRecordToCategory(ContextRecord *record);
public:
static Result SubmitContext(const ContextEntry *entry, const u8 *data, u32 data_size);
static Result SubmitContextRecord(ContextRecord *record);
static Result WriteContextsToReport(Report *report);
};
}

View file

@ -0,0 +1,155 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "erpt_srv_context_impl.hpp"
#include "erpt_srv_manager_impl.hpp"
#include "erpt_srv_context.hpp"
#include "erpt_srv_reporter.hpp"
#include "erpt_srv_journal.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());
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) {
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());
Reporter reporter(report_type, ctx, data, data_size, meta_size != 0 ? meta : nullptr, nullptr, 0);
R_TRY(reporter.CreateReport());
ManagerImpl::NotifyAll();
return ResultSuccess();
}
Result ContextImpl::SetInitialLaunchSettingsCompletionTime(const time::SteadyClockTimePoint &time_point) {
Reporter::SetInitialLaunchSettingsCompletionTime(time_point);
return ResultSuccess();
}
Result ContextImpl::ClearInitialLaunchSettingsCompletionTime() {
Reporter::ClearInitialLaunchSettingsCompletionTime();
return ResultSuccess();
}
Result ContextImpl::UpdatePowerOnTime() {
Reporter::UpdatePowerOnTime();
return ResultSuccess();
}
Result ContextImpl::UpdateAwakeTime() {
Reporter::UpdateAwakeTime();
return ResultSuccess();
}
Result ContextImpl::SubmitMultipleCategoryContext(const MultipleCategoryContextEntry &ctx_entry, const ams::sf::InBuffer &str_buffer) {
R_UNLESS(0 <= ctx_entry.category_count && 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];
}
return ResultSuccess();
}
Result ContextImpl::UpdateApplicationLaunchTime() {
Reporter::UpdateApplicationLaunchTime();
return ResultSuccess();
}
Result ContextImpl::ClearApplicationLaunchTime() {
Reporter::ClearApplicationLaunchTime();
return ResultSuccess();
}
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));
return JournalForAttachments::SubmitAttachment(out.GetPointer(), name_safe, data, data_size);
}
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) {
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());
Reporter reporter(report_type, ctx, data, data_size, nullptr, attachments, num_attachments);
R_TRY(reporter.CreateReport());
ManagerImpl::NotifyAll();
return ResultSuccess();
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::erpt::srv {
class ContextImpl final : public erpt::sf::IContext {
public:
virtual Result SubmitContext(const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer) override final;
virtual Result CreateReport(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &meta_buffer) override final;
virtual Result SetInitialLaunchSettingsCompletionTime(const time::SteadyClockTimePoint &time_point) override final;
virtual Result ClearInitialLaunchSettingsCompletionTime() override final;
virtual Result UpdatePowerOnTime() override final;
virtual Result UpdateAwakeTime() override final;
virtual Result SubmitMultipleCategoryContext(const MultipleCategoryContextEntry &ctx_entry, const ams::sf::InBuffer &str_buffer) override final;
virtual Result UpdateApplicationLaunchTime() override final;
virtual Result ClearApplicationLaunchTime() override final;
virtual Result SubmitAttachment(ams::sf::Out<AttachmentId> out, const ams::sf::InBuffer &attachment_name, const ams::sf::InBuffer &attachment_data) override final;
virtual Result CreateReportWithAttachments(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &attachment_ids_buffer) override final;
};
}

View file

@ -0,0 +1,201 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "erpt_srv_context_record.hpp"
namespace ams::erpt::srv {
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() {
this->ctx = {};
}
ContextRecord::ContextRecord(CategoryId category) {
this->ctx = {
.category = category,
.array_buffer = static_cast<u8 *>(Allocate(ArrayBufferSizeDefault)),
};
if (this->ctx.array_buffer != nullptr) {
this->ctx.array_buffer_size = ArrayBufferSizeDefault;
this->ctx.array_free_count = ArrayBufferSizeDefault;
}
}
ContextRecord::~ContextRecord() {
if (this->ctx.array_buffer != nullptr) {
Deallocate(this->ctx.array_buffer);
}
AMS_ABORT_UNLESS(s_record_count >= this->ctx.field_count);
s_record_count -= this->ctx.field_count;
}
Result ContextRecord::Initialize(const ContextEntry *ctx_ptr, const u8 *data, u32 data_size) {
R_UNLESS(data_size <= ArrayBufferSizeMax, erpt::ResultInvalidArgument());
this->ctx.version = ctx_ptr->version;
this->ctx.field_count = ctx_ptr->field_count;
this->ctx.category = ctx_ptr->category;
this->ctx.array_buffer = nullptr;
this->ctx.array_buffer_size = data_size;
this->ctx.array_free_count = 0;
auto guard = SCOPE_GUARD { this->ctx.field_count = 0; };
R_UNLESS(this->ctx.field_count <= FieldsPerContext, erpt::ResultInvalidArgument());
R_UNLESS(0 <= this->ctx.category && this->ctx.category < CategoryId_Count, erpt::ResultInvalidArgument());
for (u32 i = 0; i < this->ctx.field_count; i++) {
this->ctx.fields[i] = ctx_ptr->fields[i];
R_UNLESS(0 <= this->ctx.fields[i].id && this->ctx.fields[i].id < FieldId_Count, erpt::ResultInvalidArgument());
R_UNLESS(0 <= this->ctx.fields[i].type && this->ctx.fields[i].type < FieldType_Count, erpt::ResultInvalidArgument());
R_UNLESS(this->ctx.fields[i].type == FieldToTypeMap[this->ctx.fields[i].id], erpt::ResultFieldTypeMismatch());
R_UNLESS(this->ctx.category == FieldToCategoryMap[this->ctx.fields[i].id], erpt::ResultFieldCategoryMismatch());
if (IsArrayFieldType(this->ctx.fields[i].type)) {
const u32 start_idx = this->ctx.fields[i].value_array.start_idx;
const u32 size = this->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) {
this->ctx.array_buffer = static_cast<u8 *>(AllocateWithAlign(data_size, alignof(u64)));
R_UNLESS(this->ctx.array_buffer != nullptr, erpt::ResultOutOfMemory());
std::memcpy(this->ctx.array_buffer, data, data_size);
}
guard.Cancel();
s_record_count += this->ctx.field_count;
return ResultSuccess();
}
Result ContextRecord::Add(FieldId field_id, bool value_bool) {
R_UNLESS(this->ctx.field_count < FieldsPerContext, erpt::ResultOutOfFieldSpace());
s_record_count++;
auto &field = this->ctx.fields[this->ctx.field_count++];
field.id = field_id;
field.type = FieldType_Bool;
field.value_bool = value_bool;
return ResultSuccess();
}
Result ContextRecord::Add(FieldId field_id, u32 value_u32) {
R_UNLESS(this->ctx.field_count < FieldsPerContext, erpt::ResultOutOfFieldSpace());
s_record_count++;
auto &field = this->ctx.fields[this->ctx.field_count++];
field.id = field_id;
field.type = FieldType_NumericU32;
field.value_u32 = value_u32;
return ResultSuccess();
}
Result ContextRecord::Add(FieldId field_id, u64 value_u64) {
R_UNLESS(this->ctx.field_count < FieldsPerContext, erpt::ResultOutOfFieldSpace());
s_record_count++;
auto &field = this->ctx.fields[this->ctx.field_count++];
field.id = field_id;
field.type = FieldType_NumericU64;
field.value_u64 = value_u64;
return ResultSuccess();
}
Result ContextRecord::Add(FieldId field_id, s32 value_i32) {
R_UNLESS(this->ctx.field_count < FieldsPerContext, erpt::ResultOutOfFieldSpace());
s_record_count++;
auto &field = this->ctx.fields[this->ctx.field_count++];
field.id = field_id;
field.type = FieldType_NumericI32;
field.value_i32 = value_i32;
return ResultSuccess();
}
Result ContextRecord::Add(FieldId field_id, s64 value_i64) {
R_UNLESS(this->ctx.field_count < FieldsPerContext, erpt::ResultOutOfFieldSpace());
s_record_count++;
auto &field = this->ctx.fields[this->ctx.field_count++];
field.id = field_id;
field.type = FieldType_NumericI64;
field.value_i64 = value_i64;
return ResultSuccess();
}
Result ContextRecord::Add(FieldId field_id, const char *str, u32 str_size) {
R_UNLESS(this->ctx.field_count < FieldsPerContext, erpt::ResultOutOfFieldSpace());
R_UNLESS(str_size <= this->ctx.array_free_count, erpt::ResultOutOfArraySpace());
const u32 start_idx = this->ctx.array_buffer_size - this->ctx.array_free_count;
this->ctx.array_free_count -= str_size;
s_record_count++;
auto &field = this->ctx.fields[this->ctx.field_count++];
field.id = field_id;
field.type = FieldType_String;
field.value_array = {
.start_idx = start_idx,
.size = str_size,
};
std::memcpy(this->ctx.array_buffer + start_idx, str, str_size);
return ResultSuccess();
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "erpt_srv_allocator.hpp"
namespace ams::erpt::srv {
class Context;
class ContextRecord : public Allocator, public util::IntrusiveListBaseNode<ContextRecord> {
friend class Context;
private:
static u32 s_record_count;
public:
static u32 GetRecordCount() {
return s_record_count;
}
private:
ContextEntry ctx;
public:
ContextRecord();
explicit ContextRecord(CategoryId category);
~ContextRecord();
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);
};
}

View file

@ -0,0 +1,185 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#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 AddId(Report *report, FieldId field_id) {
static_assert(MaxFieldStringSize < ElementSize_256);
const u32 field_len = static_cast<u32>(strnlen(FieldString[field_id], MaxFieldStringSize));
if (field_len < ElementSize_32) {
R_TRY(report->Write(static_cast<u8>(static_cast<u8>(ValueTypeTag::FixStr) | field_len)));
} else {
R_TRY(report->Write(static_cast<u8>(ValueTypeTag::Str8)));
R_TRY(report->Write(static_cast<u8>(field_len)));
}
R_TRY(report->Write(FieldString[field_id], field_len));
return ResultSuccess();
}
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)));
return ResultSuccess();
}
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]));
}
return ResultSuccess();
}
template<typename T>
static Result AddIdValuePair(Report *report, FieldId field_id, T value) {
R_TRY(AddId(report, field_id));
R_TRY(AddValue(report, value));
return ResultSuccess();
}
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));
return ResultSuccess();
}
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));
}
return ResultSuccess();
}
static Result End(Report *report) {
return ResultSuccess();
}
template<typename T>
static Result AddField(Report *report, FieldId field_id, T value) {
return AddIdValuePair<T>(report, field_id, value);
}
template<typename T>
static Result AddField(Report *report, FieldId field_id, T *arr, u32 arr_size) {
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)));
return ResultSuccess();
}
static Result AddField(Report *report, FieldId field_id, char *str, u32 len) {
R_TRY(AddId(report, field_id));
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));
return ResultSuccess();
}
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));
return ResultSuccess();
}
};
}

View file

@ -0,0 +1,119 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "erpt_srv_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();
return ResultSuccess();
}
Result Journal::Delete(ReportId report_id) {
return JournalForReports::DeleteReport(report_id);
}
Result Journal::GetAttachmentList(AttachmentList *out, ReportId report_id) {
return JournalForAttachments::GetAttachmentList(out, report_id);
}
util::Uuid Journal::GetJournalId() {
return JournalForMeta::GetJournalId();
}
s64 Journal::GetMaxReportSize() {
return JournalForReports::GetMaxReportSize();
}
Result Journal::GetReportList(ReportList *out, ReportType type_filter) {
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)));
return ResultSuccess();
}
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) {
return JournalForReports::StoreRecord(record);
}
Result Journal::Store(JournalRecord<AttachmentInfo> *record) {
return JournalForAttachments::StoreRecord(record);
}
}

View file

@ -0,0 +1,119 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#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(AttachmentList *out, 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(AttachmentList *out, 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);
};
}

View file

@ -0,0 +1,219 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "erpt_srv_journal.hpp"
#include "erpt_srv_attachment.hpp"
namespace ams::erpt::srv {
util::IntrusiveListBaseTraits<JournalRecord<AttachmentInfo>>::ListType JournalForAttachments::s_attachment_list;
u32 JournalForAttachments::s_attachment_count = 0;
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->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->info)), sizeof(it->info)));
}
return ResultSuccess();
}
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->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->info.attachment_size);
/* Delete the object, if we should. */
if (record->RemoveReference()) {
Stream::DeleteStream(Attachment::FileName(record->info.attachment_id).name);
delete record;
}
} else {
/* Not attached, just advance. */
it++;
}
}
return ResultSuccess();
}
Result JournalForAttachments::GetAttachmentList(AttachmentList *out, ReportId report_id) {
u32 count = 0;
for (auto it = s_attachment_list.cbegin(); it != s_attachment_list.cend() && count < util::size(out->attachments); it++) {
if (report_id == it->info.owner_report_id) {
out->attachments[count++] = it->info;
}
}
out->attachment_count = count;
return ResultSuccess();
}
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->info.attachment_size), Attachment::FileName(record->info.attachment_id).name))) {
continue;
}
if (record->info.flags.Test<AttachmentFlag::HasOwner>() && JournalForReports::RetrieveRecord(record->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->info.attachment_id).name);
}
}
cleanup_guard.Cancel();
return ResultSuccess();
}
JournalRecord<AttachmentInfo> *JournalForAttachments::RetrieveRecord(AttachmentId attachment_id) {
for (auto it = s_attachment_list.begin(); it != s_attachment_list.end(); it++) {
return std::addressof(*it);
}
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->info.attachment_id == attachment_id) {
R_UNLESS(!record->info.flags.Test<AttachmentFlag::HasOwner>(), erpt::ResultAlreadyOwned());
record->info.owner_report_id = report_id;
record->info.flags.Set<AttachmentFlag::HasOwner>();
return ResultSuccess();
}
}
return 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->info.attachment_id != record->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->info.attachment_size);
return ResultSuccess();
}
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));
}
return ResultSuccess();
}
}

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "erpt_srv_journal.hpp"
namespace ams::erpt::srv {
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) {
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();
}
return ResultSuccess();
}
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;
}
}

View file

@ -0,0 +1,225 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "erpt_srv_journal.hpp"
#include "erpt_srv_report.hpp"
namespace ams::erpt::srv {
util::IntrusiveListBaseTraits<JournalRecord<ReportInfo>>::ListType JournalForReports::s_record_list;
u32 JournalForReports::s_record_count = 0;
u32 JournalForReports::s_record_count_by_type[ReportType_Count] = {};
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->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->info)), sizeof(it->info)));
}
return ResultSuccess();
}
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->info.type];
s_used_storage -= static_cast<u32>(record->info.report_size);
/* If we should increment count, do so. */
if (increment_count) {
JournalForMeta::IncrementCount(record->info.flags.Test<ReportFlag::Transmitted>(), record->info.type);
}
/* Delete any attachments. */
if (force_delete_attachments || record->info.flags.Test<ReportFlag::HasAttachment>()) {
JournalForAttachments::DeleteAttachments(record->info.id);
}
/* Delete the object, if we should. */
if (record->RemoveReference()) {
Stream::DeleteStream(Report::FileName(record->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->info.id == report_id) {
EraseReportImpl(record, false, false);
return ResultSuccess();
}
}
return 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->info.flags.Test<ReportFlag::HasAttachment>()) {
EraseReportImpl(record, true, true);
return ResultSuccess();
}
}
return erpt::ResultNotFound();
}
s64 JournalForReports::GetMaxReportSize() {
s64 max_size;
for (auto it = s_record_list.begin(); it != s_record_list.end(); it++) {
max_size = std::max(max_size, it->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->info.type) {
out->reports[count++] = it->info;
}
}
out->report_count = count;
return ResultSuccess();
}
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->info.report_size == 0) {
R_UNLESS(R_SUCCEEDED(Stream::GetStreamSize(std::addressof(record->info.report_size), Report::FileName(record->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();
return ResultSuccess();
}
JournalRecord<ReportInfo> *JournalForReports::RetrieveRecord(ReportId report_id) {
for (auto it = s_record_list.begin(); it != s_record_list.end(); it++) {
return std::addressof(*it);
}
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->info.id != record->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->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->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->info.type]++;
s_used_storage += static_cast<u32>(record->info.report_size);
return ResultSuccess();
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#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 info;
JournalRecord() {
std::memset(std::addressof(this->info), 0, sizeof(this->info));
}
explicit JournalRecord(Info info) : info(info) { /* ... */ }
};
}

View file

@ -0,0 +1,98 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "erpt_srv_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() {
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);
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::erpt::srv {
const u8 *GetPublicKeyModulus();
size_t GetPublicKeyModulusSize();
const u8 *GetPublicKeyExponent();
size_t GetPublicKeyExponentSize();
}

View file

@ -0,0 +1,132 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "erpt_srv_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"
namespace ams::erpt::srv {
lmem::HeapHandle g_heap_handle;
namespace {
constexpr fs::SystemSaveDataId SystemSaveDataId = 0x80000000000000D1;
constexpr u32 SystemSaveDataFlags = fs::SaveDataFlags_KeepAfterResettingSystemSaveDataWithoutUserSaveData;
constexpr s64 SystemSaveDataSize = 11_MB;
constexpr s64 SystemSaveDataJournalSize = 2720_KB;
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_300) {
R_TRY(fs::ExtendSaveData(fs::SaveDataSpaceId::System, SystemSaveDataId, SystemSaveDataSize, SystemSaveDataJournalSize));
}
}
return ResultSuccess();
}
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;
return ResultSuccess();
}
}
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::SetAllocator(Allocate, DeallocateWithSize);
R_ABORT_UNLESS(fs::MountSdCardErrorReportDirectoryForAtmosphere(ReportOnSdStoragePath));
R_ABORT_UNLESS(MountSystemSaveData());
for (auto i = 0; i < CategoryId_Count; i++) {
Context *ctx = new Context(static_cast<CategoryId>(i), 1);
AMS_ABORT_UNLESS(ctx != nullptr);
}
Journal::Restore();
return ResultSuccess();
}
Result InitializeAndStartService() {
return InitializeService();
}
Result SetSerialNumberAndOsVersion(const char *sn, u32 sn_len, const char *os, u32 os_len, const char *os_priv, u32 os_priv_len) {
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 = new ContextRecord(CategoryId_ProductModelInfo);
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
R_TRY(record->Add(FieldId_ProductModel, model, model_len));
R_TRY(Context::SubmitContextRecord(record));
return ResultSuccess();
}
Result SetRegionSetting(const char *region, u32 region_len) {
/* NOTE: Nintendo does not check that this allocation succeeds. */
auto *record = new ContextRecord(CategoryId_RegionSettingInfo);
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
R_TRY(record->Add(FieldId_RegionSetting, region, region_len));
R_TRY(Context::SubmitContextRecord(record));
return ResultSuccess();
}
Result SetRedirectNewReportsToSdCard(bool redirect) {
Reporter::SetRedirectNewReportsToSdCard(redirect);
return ResultSuccess();
}
void Wait() {
return WaitService();
}
}

View file

@ -0,0 +1,95 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "erpt_srv_manager_impl.hpp"
#include "erpt_srv_journal.hpp"
namespace ams::erpt::srv {
namespace {
using ManagerList = util::IntrusiveListBaseTraits<ManagerImpl>::ListType;
ManagerList g_manager_list;
}
ManagerImpl::ManagerImpl() : 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() {
this->system_event.Signal();
}
Result ManagerImpl::NotifyAll() {
for (auto &manager : g_manager_list) {
manager.NotifyOne();
}
return ResultSuccess();
}
Result ManagerImpl::GetReportList(const ams::sf::OutBuffer &out_list, ReportType type_filter) {
R_UNLESS(out_list.GetSize() == sizeof(ReportList), erpt::ResultInvalidArgument());
return Journal::GetReportList(reinterpret_cast<ReportList *>(out_list.GetPointer()), type_filter);
}
Result ManagerImpl::GetEvent(ams::sf::OutCopyHandle out) {
out.SetValue(this->system_event.GetReadableHandle());
return ResultSuccess();
}
Result ManagerImpl::CleanupReports() {
Journal::CleanupReports();
Journal::CleanupAttachments();
return Journal::Commit();
}
Result ManagerImpl::DeleteReport(const ReportId &report_id) {
R_TRY(Journal::Delete(report_id));
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);
return ResultSuccess();
}
Result ManagerImpl::GetAttachmentList(const ams::sf::OutBuffer &out_list, const ReportId &report_id) {
R_UNLESS(out_list.GetSize() == sizeof(AttachmentList), erpt::ResultInvalidArgument());
return Journal::GetAttachmentList(reinterpret_cast<AttachmentList *>(out_list.GetPointer()), report_id);
}
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::erpt::srv {
class ManagerImpl final : public erpt::sf::IManager, public util::IntrusiveListBaseNode<ManagerImpl> {
private:
os::SystemEvent system_event;
public:
ManagerImpl();
~ManagerImpl();
private:
void NotifyOne();
public:
static Result NotifyAll();
public:
virtual Result GetReportList(const ams::sf::OutBuffer &out_list, ReportType type_filter) override final;
virtual Result GetEvent(ams::sf::OutCopyHandle out) override final;
virtual Result CleanupReports() override final;
virtual Result DeleteReport(const ReportId &report_id) override final;
virtual Result GetStorageUsageStatistics(ams::sf::Out<StorageUsageStatistics> out) override final;
virtual Result GetAttachmentList(const ams::sf::OutBuffer &out_buf, const ReportId &report_id) override final;
};
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::erpt::srv {
class RefCount {
private:
static constexpr u32 MaxReferenceCount = 1000;
std::atomic<u32> ref_count;
public:
RefCount() : ref_count(0) { /* ... */ }
void AddReference() {
const auto prev = this->ref_count.fetch_add(1);
AMS_ABORT_UNLESS(prev <= MaxReferenceCount);
}
bool RemoveReference() {
auto prev = this->ref_count.fetch_sub(1);
AMS_ABORT_UNLESS(prev != 0);
return prev == 1;
}
};
}

View file

@ -0,0 +1,90 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "erpt_srv_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;
std::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) : record(r), redirect_to_sd_card(redirect_to_sd) {
this->record->AddReference();
}
Report::~Report() {
this->CloseStream();
if (this->record->RemoveReference()) {
this->DeleteStream(this->FileName().name);
delete this->record;
}
}
ReportFileName Report::FileName() {
return FileName(this->record->info.id, this->redirect_to_sd_card);
}
Result Report::Open(ReportOpenType type) {
switch (type) {
case ReportOpenType_Create: return this->OpenStream(this->FileName().name, StreamMode_Write, ReportStreamBufferSize);
case ReportOpenType_Read: return this->OpenStream(this->FileName().name, StreamMode_Read, ReportStreamBufferSize);
default: return erpt::ResultInvalidArgument();
}
}
Result Report::Read(u32 *out_read_count, u8 *dst, u32 dst_size) {
return this->ReadStream(out_read_count, dst, dst_size);
}
Result Report::Delete() {
return this->DeleteStream(this->FileName().name);
}
void Report::Close() {
return this->CloseStream();
}
Result Report::GetFlags(ReportFlagSet *out) {
*out = this->record->info.flags;
return ResultSuccess();
}
Result Report::SetFlags(ReportFlagSet flags) {
if (((~this->record->info.flags) & flags).IsAnySet()) {
this->record->info.flags |= flags;
return Journal::Commit();
}
return ResultSuccess();
}
Result Report::GetSize(s64 *out) {
return this->GetStreamSize(out);
}
}

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#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> *record;
bool redirect_to_sd_card;
private:
ReportFileName FileName();
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);
Result SetFlags(ReportFlagSet flags);
Result GetSize(s64 *out);
template<typename T>
Result Write(T val) {
return this->WriteStream(reinterpret_cast<const u8 *>(std::addressof(val)), sizeof(val));
}
template<typename T>
Result Write(const T *buf, u32 buffer_size) {
return this->WriteStream(reinterpret_cast<const u8 *>(buf), buffer_size);
}
};
}

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "erpt_srv_report_impl.hpp"
#include "erpt_srv_report.hpp"
namespace ams::erpt::srv {
ReportImpl::ReportImpl() : report(nullptr) {
/* ... */
}
ReportImpl::~ReportImpl() {
R_ABORT_UNLESS(this->Close());
}
Result ReportImpl::Open(const ReportId &report_id) {
R_UNLESS(this->report == nullptr, erpt::ResultAlreadyInitialized());
JournalRecord<ReportInfo> *record = Journal::Retrieve(report_id);
R_UNLESS(record != nullptr, erpt::ResultNotFound());
this->report = new Report(record, false);
R_UNLESS(this->report != nullptr, erpt::ResultOutOfMemory());
auto report_guard = SCOPE_GUARD { delete this->report; this->report = nullptr; };
R_TRY(this->report->Open(ReportOpenType_Read));
report_guard.Cancel();
return ResultSuccess();
}
Result ReportImpl::Read(ams::sf::Out<u32> out_count, const ams::sf::OutBuffer &out_buffer) {
R_UNLESS(this->report != nullptr, erpt::ResultNotInitialized());
return this->report->Read(out_count.GetPointer(), static_cast<u8 *>(out_buffer.GetPointer()), static_cast<u32>(out_buffer.GetSize()));
}
Result ReportImpl::SetFlags(ReportFlagSet flags) {
R_UNLESS(this->report != nullptr, erpt::ResultNotInitialized());
return this->report->SetFlags(flags);
}
Result ReportImpl::GetFlags(ams::sf::Out<ReportFlagSet> out) {
R_UNLESS(this->report != nullptr, erpt::ResultNotInitialized());
return this->report->GetFlags(out.GetPointer());
}
Result ReportImpl::Close() {
if (this->report != nullptr) {
this->report->Close();
delete this->report;
this->report = nullptr;
}
return ResultSuccess();
}
Result ReportImpl::GetSize(ams::sf::Out<s64> out) {
R_UNLESS(this->report != nullptr, erpt::ResultNotInitialized());
return this->report->GetSize(out.GetPointer());
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::erpt::srv {
class Report;
class ReportImpl final : public erpt::sf::IReport {
private:
Report *report;
public:
ReportImpl();
~ReportImpl();
public:
virtual Result Open(const ReportId &report_id) override final;
virtual Result Read(ams::sf::Out<u32> out_count, const ams::sf::OutBuffer &out_buffer) override final;
virtual Result SetFlags(ReportFlagSet flags) override final;
virtual Result GetFlags(ams::sf::Out<ReportFlagSet> out) override final;
virtual Result Close() override final;
virtual Result GetSize(ams::sf::Out<s64> out) override final;
};
}

View file

@ -0,0 +1,226 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "erpt_srv_reporter.hpp"
#include "erpt_srv_report.hpp"
#include "erpt_srv_journal.hpp"
#include "erpt_srv_context_record.hpp"
#include "erpt_srv_context.hpp"
namespace ams::erpt::srv {
bool Reporter::s_redirect_new_reports = true;
char Reporter::s_serial_number[24] = "Unknown";
char Reporter::s_os_version[24] = "Unknown";
char Reporter::s_private_os_version[96] = "Unknown";
std::optional<os::Tick> Reporter::s_application_launch_time;
std::optional<os::Tick> Reporter::s_awake_time;
std::optional<os::Tick> Reporter::s_power_on_time;
std::optional<time::SteadyClockTimePoint> Reporter::s_initial_launch_settings_completion_time;
Reporter::Reporter(ReportType type, const ContextEntry *ctx, const u8 *data, u32 data_size, const ReportMetaData *meta, const AttachmentId *attachments, u32 num_attachments)
: type(type), ctx(ctx), data(data), data_size(data_size), meta(meta), attachments(attachments), num_attachments(num_attachments), occurrence_tick()
{
/* ... */
}
Result Reporter::CreateReport() {
R_TRY(this->ValidateReportContext());
R_TRY(this->CollectUniqueReportFields());
R_TRY(this->SubmitReportDefaults());
R_TRY(this->SubmitReportContexts());
R_TRY(this->LinkAttachments());
R_TRY(this->CreateReportFile());
this->SaveSyslogReportIfRequired();
return ResultSuccess();
}
Result Reporter::ValidateReportContext() {
R_UNLESS(this->ctx->category == CategoryId_ErrorInfo, erpt::ResultRequiredContextMissing());
R_UNLESS(this->ctx->field_count <= FieldsPerContext, erpt::ResultInvalidArgument());
bool found_error_code = false;
for (u32 i = 0; i < this->ctx->field_count; i++) {
if (this->ctx->fields[i].id == FieldId_ErrorCode) {
found_error_code = true;
break;
}
}
R_UNLESS(found_error_code, erpt::ResultRequiredFieldMissing());
return ResultSuccess();
}
Result Reporter::CollectUniqueReportFields() {
this->occurrence_tick = os::GetSystemTick();
if (hos::GetVersion() >= hos::Version_300) {
this->steady_clock_internal_offset_seconds = time::GetStandardSteadyClockInternalOffset().GetSeconds();
} else {
this->steady_clock_internal_offset_seconds = 0;
}
this->report_id.uuid = util::GenerateUuid();
this->report_id.uuid.ToString(this->identifier_str, sizeof(this->identifier_str));
if (R_FAILED(time::StandardNetworkSystemClock::GetCurrentTime(std::addressof(this->timestamp_network)))) {
this->timestamp_network = {0};
}
R_ABORT_UNLESS(time::GetStandardSteadyClockCurrentTimePoint(std::addressof(this->steady_clock_current_timepoint)));
R_TRY(time::StandardUserSystemClock::GetCurrentTime(std::addressof(this->timestamp_user)));
return ResultSuccess();
}
Result Reporter::SubmitReportDefaults() {
bool found_abort_flag = false, found_syslog_flag = false;
for (u32 i = 0; i < this->ctx->field_count; i++) {
if (this->ctx->fields[i].id == FieldId_AbortFlag) {
found_abort_flag = true;
}
if (this->ctx->fields[i].id == FieldId_HasSyslogFlag) {
found_syslog_flag = true;
}
if (found_abort_flag && found_syslog_flag) {
break;
}
}
ContextRecord *record = new ContextRecord(CategoryId_ErrorInfoDefaults);
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
auto record_guard = SCOPE_GUARD { delete record; };
if (!found_abort_flag) {
record->Add(FieldId_AbortFlag, false);
}
if (!found_syslog_flag) {
record->Add(FieldId_HasSyslogFlag, true);
}
R_TRY(Context::SubmitContextRecord(record));
record_guard.Cancel();
return ResultSuccess();
}
Result Reporter::SubmitReportContexts() {
ContextRecord *record = new ContextRecord(CategoryId_ErrorInfoAuto);
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
auto record_guard = SCOPE_GUARD { delete record; };
record->Add(FieldId_OsVersion, s_os_version, strnlen(s_os_version, sizeof(s_os_version)));
record->Add(FieldId_PrivateOsVersion, s_private_os_version, strnlen(s_private_os_version, sizeof(s_private_os_version)));
record->Add(FieldId_SerialNumber, s_serial_number, strnlen(s_serial_number, sizeof(s_serial_number)));
record->Add(FieldId_ReportIdentifier, this->identifier_str, sizeof(this->identifier_str));
record->Add(FieldId_OccurrenceTimestamp, this->timestamp_user.value);
record->Add(FieldId_OccurrenceTimestampNet, this->timestamp_network.value);
record->Add(FieldId_ReportVisibilityFlag, this->type == ReportType_Visible);
record->Add(FieldId_OccurrenceTick, this->occurrence_tick.GetInt64Value());
record->Add(FieldId_SteadyClockInternalOffset, this->steady_clock_internal_offset_seconds);
record->Add(FieldId_SteadyClockCurrentTimePointValue, this->steady_clock_current_timepoint.value);
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, this->steady_clock_current_timepoint))) {
record->Add(FieldId_ElapsedTimeSinceInitialLaunch, elapsed_seconds);
}
}
if (s_power_on_time) {
record->Add(FieldId_ElapsedTimeSincePowerOn, (this->occurrence_tick - *s_power_on_time).ToTimeSpan().GetSeconds());
}
if (s_awake_time) {
record->Add(FieldId_ElapsedTimeSincePowerOn, (this->occurrence_tick - *s_awake_time).ToTimeSpan().GetSeconds());
}
if (s_application_launch_time) {
record->Add(FieldId_ApplicationAliveTime, (this->occurrence_tick - *s_application_launch_time).ToTimeSpan().GetSeconds());
}
R_TRY(Context::SubmitContextRecord(record));
record_guard.Cancel();
R_TRY(Context::SubmitContext(this->ctx, this->data, this->data_size));
return ResultSuccess();
}
Result Reporter::LinkAttachments() {
for (u32 i = 0; i < this->num_attachments; i++) {
R_TRY(JournalForAttachments::SetOwner(this->attachments[i], this->report_id));
}
return ResultSuccess();
}
Result Reporter::CreateReportFile() {
/* Make a journal record. */
auto *record = new JournalRecord<ReportInfo>;
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
record->AddReference();
ON_SCOPE_EXIT {
if (record->RemoveReference()) {
delete record;
}
};
record->info.type = this->type;
record->info.id = this->report_id;
record->info.flags = erpt::srv::MakeNoReportFlags();
record->info.timestamp_user = this->timestamp_user;
record->info.timestamp_network = this->timestamp_network;
if (this->meta != nullptr) {
record->info.meta_data = *this->meta;
}
if (this->num_attachments > 0) {
record->info.flags.Set<ReportFlag::HasAttachment>();
}
auto report = std::make_unique<Report>(record, s_redirect_new_reports);
R_UNLESS(report != nullptr, erpt::ResultOutOfMemory());
R_TRY(Context::WriteContextsToReport(report.get()));
R_TRY(report->GetSize(std::addressof(record->info.report_size)));
if (!s_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));
} 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(this->report_id));
}
R_TRY(Journal::Commit());
return ResultSuccess();
}
void Reporter::SaveSyslogReportIfRequired() {
bool needs_save_syslog = true;
for (u32 i = 0; i < this->ctx->field_count; i++) {
static_assert(FieldToTypeMap[FieldId_HasSyslogFlag] == FieldType_Bool);
if (this->ctx->fields[i].id == FieldId_HasSyslogFlag && (this->ctx->fields[i].value_bool == false)) {
needs_save_syslog = false;
break;
}
}
if (needs_save_syslog) {
/* Here nintendo sends a report to srepo:u (vtable offset 0xE8) with data this->report_id. */
/* We will not send report ids to srepo:u. */
}
}
}

View file

@ -0,0 +1,83 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::erpt::srv {
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 std::optional<os::Tick> s_application_launch_time;
static std::optional<os::Tick> s_awake_time;
static std::optional<os::Tick> s_power_on_time;
static std::optional<time::SteadyClockTimePoint> s_initial_launch_settings_completion_time;
private:
const ReportType type;
const ContextEntry * const ctx;
const u8 * const data;
const u32 data_size;
const ReportMetaData * const meta;
const AttachmentId * const attachments;
const u32 num_attachments;
char identifier_str[0x40];
time::PosixTime timestamp_user;
time::PosixTime timestamp_network;
os::Tick occurrence_tick;
s64 steady_clock_internal_offset_seconds;
ReportId report_id;
time::SteadyClockTimePoint steady_clock_current_timepoint;
public:
static void ClearApplicationLaunchTime() { s_application_launch_time = std::nullopt; }
static void ClearInitialLaunchSettingsCompletionTime() { s_initial_launch_settings_completion_time = std::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);
return ResultSuccess();
}
static void SetRedirectNewReportsToSdCard(bool en) { s_redirect_new_reports = en; }
private:
Result ValidateReportContext();
Result CollectUniqueReportFields();
Result SubmitReportDefaults();
Result SubmitReportContexts();
Result LinkAttachments();
Result CreateReportFile();
void SaveSyslogReportIfRequired();
void SaveSyslogReport();
public:
Reporter(ReportType type, const ContextEntry *ctx, const u8 *data, u32 data_size, const ReportMetaData *meta, const AttachmentId *attachments, u32 num_attachments);
Result CreateReport();
};
}

View file

@ -0,0 +1,134 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "erpt_srv_service.hpp"
#include "erpt_srv_context_impl.hpp"
#include "erpt_srv_session_impl.hpp"
#include "erpt_srv_stream.hpp"
namespace ams::erpt::srv {
namespace {
struct ErrorReportServerOptions {
static constexpr size_t PointerBufferSize = 0;
static constexpr size_t MaxDomains = 64;
static constexpr size_t MaxDomainObjects = 2 * ReportCountMax + 5 + 2;
};
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 s32 ErrorReportServerThreadPriority = 21;
constexpr inline sm::ServiceName ErrorReportContextServiceName = sm::ServiceName::Encode("erpt:c");
constexpr inline sm::ServiceName ErrorReportReportServiceName = sm::ServiceName::Encode("erpt:r");
alignas(os::ThreadStackAlignment) u8 g_server_thread_stack[16_KB];
class ErrorReportServiceManager : public ams::sf::hipc::ServerManager<ErrorReportNumServers, ErrorReportServerOptions, ErrorReportMaxSessions> {
private:
os::ThreadType thread;
std::shared_ptr<erpt::srv::ContextImpl> context_session_object;
private:
static void ThreadFunction(void *_this) {
reinterpret_cast<ErrorReportServiceManager *>(_this)->SetupAndLoopProcess();
}
void SetupAndLoopProcess();
public:
ErrorReportServiceManager(erpt::srv::ContextImpl *c)
: context_session_object(ams::sf::ServiceObjectTraits<erpt::srv::ContextImpl>::SharedPointerHelper::GetEmptyDeleteSharedPointer(c))
{
/* ... */
}
Result Initialize() {
R_ABORT_UNLESS(this->RegisterServer<erpt::srv::ContextImpl>(ErrorReportContextServiceName, ErrorReportContextSessions, this->context_session_object));
R_ABORT_UNLESS(this->RegisterServer<erpt::srv::SessionImpl>(ErrorReportReportServiceName, ErrorReportReportSessions));
this->ResumeProcessing();
R_ABORT_UNLESS(os::CreateThread(std::addressof(this->thread), ThreadFunction, this, g_server_thread_stack, sizeof(g_server_thread_stack), ErrorReportServerThreadPriority));
os::StartThread(std::addressof(this->thread));
return ResultSuccess();
}
void Wait() {
os::WaitThread(std::addressof(this->thread));
}
};
void ErrorReportServiceManager::SetupAndLoopProcess() {
const psc::PmModuleId dependencies[] = { psc::PmModuleId_Fs };
psc::PmModule pm_module;
psc::PmState pm_state;
psc::PmFlagSet pm_flags;
os::WaitableHolderType module_event_holder;
R_ABORT_UNLESS(pm_module.Initialize(psc::PmModuleId_Erpt, dependencies, util::size(dependencies), os::EventClearMode_ManualClear));
os::InitializeWaitableHolder(std::addressof(module_event_holder), pm_module.GetEventPointer()->GetBase());
os::SetWaitableHolderUserData(std::addressof(module_event_holder), static_cast<uintptr_t>(psc::PmModuleId_Erpt));
this->AddUserWaitableHolder(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_Awake:
case psc::PmState_ReadyAwaken:
Stream::EnableFsAccess(true);
break;
case psc::PmState_ReadySleep:
case psc::PmState_ReadyShutdown:
Stream::EnableFsAccess(false);
break;
default:
break;
}
R_ABORT_UNLESS(pm_module.Acknowledge(pm_state, ResultSuccess()));
} else {
AMS_ASSERT(false);
}
this->AddUserWaitableHolder(signaled_holder);
}
}
}
erpt::srv::ContextImpl g_context_object;
ErrorReportServiceManager g_erpt_server_manager(std::addressof(g_context_object));
}
Result InitializeService() {
return g_erpt_server_manager.Initialize();
}
void WaitService() {
return g_erpt_server_manager.Wait();
}
}

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::erpt::srv {
Result InitializeService();
void WaitService();
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "erpt_srv_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 {
Result SessionImpl::OpenReport(ams::sf::Out<std::shared_ptr<erpt::sf::IReport>> out) {
/* Create an interface. */
auto intf = std::shared_ptr<ReportImpl>(new (std::nothrow) ReportImpl);
R_UNLESS(intf != nullptr, erpt::ResultOutOfMemory());
/* Return it. */
out.SetValue(std::move(intf));
return ResultSuccess();
}
Result SessionImpl::OpenManager(ams::sf::Out<std::shared_ptr<erpt::sf::IManager>> out) {
/* Create an interface. */
auto intf = std::shared_ptr<ManagerImpl>(new (std::nothrow) ManagerImpl);
R_UNLESS(intf != nullptr, erpt::ResultOutOfMemory());
/* Return it. */
out.SetValue(std::move(intf));
return ResultSuccess();
}
Result SessionImpl::OpenAttachment(ams::sf::Out<std::shared_ptr<erpt::sf::IAttachment>> out) {
/* Create an interface. */
auto intf = std::shared_ptr<AttachmentImpl>(new (std::nothrow) AttachmentImpl);
R_UNLESS(intf != nullptr, erpt::ResultOutOfMemory());
/* Return it. */
out.SetValue(std::move(intf));
return ResultSuccess();
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::erpt::srv {
class SessionImpl final : public erpt::sf::ISession {
public:
virtual Result OpenReport(ams::sf::Out<std::shared_ptr<erpt::sf::IReport>> out) override final;
virtual Result OpenManager(ams::sf::Out<std::shared_ptr<erpt::sf::IManager>> out) override final;
virtual Result OpenAttachment(ams::sf::Out<std::shared_ptr<erpt::sf::IAttachment>> out) override final;
};
}

View file

@ -0,0 +1,188 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "erpt_srv_allocator.hpp"
#include "erpt_srv_stream.hpp"
namespace ams::erpt::srv {
bool Stream::s_can_access_fs = true;
void Stream::EnableFsAccess(bool en) {
s_can_access_fs = en;
}
Result Stream::DeleteStream(const char *path) {
R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState());
return fs::DeleteFile(path);
}
Result Stream::CommitStream() {
R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState());
fs::CommitSaveData(ReportStoragePath);
return ResultSuccess();
}
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); };
return fs::GetFileSize(out, file);
}
Stream::Stream() : buffer_size(0), file_position(0), buffer_count(0), buffer(nullptr), stream_mode(StreamMode_Invalid), initialized(false) {
/* ... */
}
Stream::~Stream() {
this->CloseStream();
}
Result Stream::OpenStream(const char *path, StreamMode mode, u32 buffer_size) {
R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState());
R_UNLESS(!this->initialized, erpt::ResultAlreadyInitialized());
if (mode == StreamMode_Write) {
while (true) {
R_TRY_CATCH(fs::OpenFile(std::addressof(this->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(this->file_handle, 0);
} else {
R_UNLESS(mode == StreamMode_Read, erpt::ResultInvalidArgument());
}
auto file_guard = SCOPE_GUARD { if (mode == StreamMode_Write) { fs::CloseFile(this->file_handle); } };
std::strncpy(this->file_name, path, sizeof(this->file_name));
this->buffer = reinterpret_cast<u8 *>(Allocate(buffer_size));
R_UNLESS(this->buffer != nullptr, erpt::ResultOutOfMemory());
this->buffer_size = buffer_size;
this->buffer_count = 0;
this->buffer_position = 0;
this->file_position = 0;
this->stream_mode = mode;
this->initialized = true;
file_guard.Cancel();
return ResultSuccess();
}
Result Stream::ReadStream(u32 *out, u8 *dst, u32 dst_size) {
R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState());
R_UNLESS(this->initialized, erpt::ResultNotInitialized());
R_UNLESS(this->stream_mode == StreamMode_Read, erpt::ResultNotInitialized());
R_UNLESS(out != nullptr, erpt::ResultInvalidArgument());
R_UNLESS(dst != nullptr, erpt::ResultInvalidArgument());
fs::FileHandle tmp_file;
size_t fs_read_size;
u32 read_count = 0;
bool opened = false;
ON_SCOPE_EXIT {
*out = read_count;
if (opened) {
fs::CloseFile(tmp_file);
}
};
while (dst_size > 0) {
if (u32 cur = std::min<u32>(this->buffer_count - this->buffer_position, dst_size); cur > 0) {
std::memcpy(dst, this->buffer + this->buffer_position, cur);
this->buffer_position += cur;
dst += cur;
dst_size -= cur;
read_count += cur;
} else {
if (!opened) {
R_TRY(fs::OpenFile(std::addressof(tmp_file), this->file_name, fs::OpenMode_Read));
opened = true;
}
R_TRY(fs::ReadFile(std::addressof(fs_read_size), tmp_file, this->file_position, this->buffer, this->buffer_size));
this->buffer_position = 0;
this->file_position += static_cast<u32>(fs_read_size);
this->buffer_count = static_cast<u32>(fs_read_size);
if (this->buffer_count == 0) {
break;
}
}
}
return ResultSuccess();
}
Result Stream::WriteStream(const u8 *src, u32 src_size) {
R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState());
R_UNLESS(this->initialized, erpt::ResultNotInitialized());
R_UNLESS(this->stream_mode == StreamMode_Write, erpt::ResultNotInitialized());
R_UNLESS(src != nullptr || src_size == 0, erpt::ResultInvalidArgument());
while (src_size > 0) {
if (u32 cur = std::min<u32>(this->buffer_size - this->buffer_count, src_size); cur > 0) {
std::memcpy(this->buffer + this->buffer_count, src, cur);
this->buffer_count += cur;
src += cur;
src_size -= cur;
}
if (this->buffer_count == this->buffer_size) {
R_TRY(this->Flush());
}
}
return ResultSuccess();
}
void Stream::CloseStream() {
if (this->initialized) {
if (s_can_access_fs && this->stream_mode == StreamMode_Write) {
this->Flush();
fs::FlushFile(this->file_handle);
fs::CloseFile(this->file_handle);
}
Deallocate(this->buffer);
this->initialized = false;
}
}
Result Stream::GetStreamSize(s64 *out) {
return GetStreamSize(out, this->file_name);
}
Result Stream::Flush() {
R_SUCCEED_IF(this->buffer_count == 0);
R_TRY(fs::WriteFile(this->file_handle, this->file_position, this->buffer, this->buffer_count, fs::WriteOption::None));
this->file_position += this->buffer_count;
this->buffer_count = 0;
return ResultSuccess();
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::erpt::srv {
enum StreamMode {
StreamMode_Write = 0,
StreamMode_Read = 1,
StreamMode_Invalid = 2,
};
class Stream {
private:
static bool s_can_access_fs;
private:
u32 buffer_size;
u32 file_position;
u32 buffer_position;
u32 buffer_count;
u8 *buffer;
StreamMode stream_mode;
bool initialized;
fs::FileHandle file_handle;
char 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);
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);
};
}

View file

@ -116,4 +116,24 @@ namespace ams::fs {
return impl::WriteSaveDataFileSystemExtraData(space_id, id, extra_data); return impl::WriteSaveDataFileSystemExtraData(space_id, id, extra_data);
} }
Result GetSaveDataAvailableSize(s64 *out, SaveDataId id) {
SaveDataExtraData extra_data;
R_TRY(impl::ReadSaveDataFileSystemExtraData(std::addressof(extra_data), id));
*out = extra_data.available_size;
return ResultSuccess();
}
Result GetSaveDataJournalSize(s64 *out, SaveDataId id) {
SaveDataExtraData extra_data;
R_TRY(impl::ReadSaveDataFileSystemExtraData(std::addressof(extra_data), id));
*out = extra_data.journal_size;
return ResultSuccess();
}
Result ExtendSaveData(SaveDataSpaceId space_id, SaveDataId id, s64 available_size, s64 journal_size) {
return ::fsExtendSaveDataFileSystem(static_cast<::FsSaveDataSpaceId>(space_id), static_cast<u64>(id), available_size, journal_size);
}
} }

View file

@ -20,6 +20,8 @@ namespace ams::fs {
namespace { namespace {
constexpr inline const char AtmosphereErrorReportDirectory[] = "/atmosphere/erpt_reports";
/* NOTE: Nintendo does not attach a generator to a mounted SD card filesystem. */ /* NOTE: Nintendo does not attach a generator to a mounted SD card filesystem. */
/* However, it is desirable for homebrew to be able to access SD via common path. */ /* However, it is desirable for homebrew to be able to access SD via common path. */
class SdCardCommonMountNameGenerator : public fsa::ICommonMountNameGenerator, public impl::Newable { class SdCardCommonMountNameGenerator : public fsa::ICommonMountNameGenerator, public impl::Newable {
@ -63,4 +65,27 @@ namespace ams::fs {
return fsa::Register(name, std::move(fsa), std::move(generator)); return fsa::Register(name, std::move(fsa), std::move(generator));
} }
Result MountSdCardErrorReportDirectoryForAtmosphere(const char *name) {
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
/* Open the SD card. This uses libnx bindings. */
FsFileSystem fs;
R_TRY(fsOpenSdCardFileSystem(std::addressof(fs)));
/* Allocate a new filesystem wrapper. */
std::unique_ptr<fsa::IFileSystem> fsa = std::make_unique<RemoteFileSystem>(fs);
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInSdCardA());
/* Ensure that the error report directory exists. */
R_TRY(fssystem::EnsureDirectoryRecursively(fsa.get(), AtmosphereErrorReportDirectory));
/* Create a subdirectory filesystem. */
auto subdir_fs = std::make_unique<fssystem::SubDirectoryFileSystem>(std::move(fsa), AtmosphereErrorReportDirectory);
R_UNLESS(subdir_fs != nullptr, fs::ResultAllocationFailureInSdCardA());
/* Register. */
return fsa::Register(name, std::move(subdir_fs));
}
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019-2020 Adubbz, Atmosphère-NX * Copyright (c) 2019-2020 Atmosphère-NX
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License, * under the terms and conditions of the GNU General Public License,

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "psc_remote_pm_module.hpp"
namespace ams::psc {
PmModule::PmModule() : intf(nullptr), initialized(false), reserved(0) { /* ... */ }
PmModule::~PmModule() {
if (this->initialized) {
this->intf = nullptr;
os::DestroySystemEvent(this->system_event.GetBase());
}
}
Result PmModule::Initialize(const PmModuleId mid, const PmModuleId *dependencies, u32 dependency_count, os::EventClearMode clear_mode) {
R_UNLESS(!this->initialized, psc::ResultAlreadyInitialized());
static_assert(sizeof(*dependencies) == sizeof(u16));
::PscPmModule module;
R_TRY(::pscmGetPmModule(std::addressof(module), static_cast<::PscPmModuleId>(mid), reinterpret_cast<const u16 *>(dependencies), dependency_count, clear_mode == os::EventClearMode_AutoClear));
this->intf = std::make_shared<RemotePmModule>(module);
this->system_event.AttachReadableHandle(module.event.revent, false, clear_mode);
this->initialized = true;
return ResultSuccess();
}
Result PmModule::Finalize() {
R_UNLESS(this->initialized, psc::ResultNotInitialized());
R_TRY(this->intf->Finalize());
this->intf = nullptr;
os::DestroySystemEvent(this->system_event.GetBase());
this->initialized = false;
return ResultSuccess();
}
Result PmModule::GetRequest(PmState *out_state, PmFlagSet *out_flags) {
R_UNLESS(this->initialized, psc::ResultNotInitialized());
return this->intf->GetRequest(out_state, out_flags);
}
Result PmModule::Acknowledge(PmState state, Result res) {
R_ABORT_UNLESS(res);
R_UNLESS(this->initialized, psc::ResultNotInitialized());
if (hos::GetVersion() >= hos::Version_600) {
return this->intf->AcknowledgeEx(state);
} else {
return this->intf->Acknowledge();
}
}
os::SystemEvent *PmModule::GetEventPointer() {
return std::addressof(this->system_event);
}
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::psc {
class RemotePmModule final : public psc::sf::IPmModule {
NON_COPYABLE(RemotePmModule);
NON_MOVEABLE(RemotePmModule);
private:
::PscPmModule module;
public:
constexpr RemotePmModule(const ::PscPmModule &m) : module(m) { /* ... */ }
virtual ~RemotePmModule() override {
::pscPmModuleClose(std::addressof(this->module));
}
virtual Result Initialize(ams::sf::OutCopyHandle out, psc::PmModuleId module_id, const ams::sf::InBuffer &child_list) override final {
/* NOTE: This functionality is already implemented by the libnx command we use to instantiate the PscPmModule. */
AMS_ABORT();
}
virtual Result GetRequest(ams::sf::Out<PmState> out_state, ams::sf::Out<PmFlagSet> out_flags) override final {
static_assert(sizeof(PmState) == sizeof(::PscPmState));
static_assert(sizeof(PmFlagSet) == sizeof(u32));
return ::pscPmModuleGetRequest(std::addressof(this->module), reinterpret_cast<::PscPmState *>(out_state.GetPointer()), reinterpret_cast<u32 *>(out_flags.GetPointer()));
}
virtual Result Acknowledge() override final {
/* NOTE: libnx does not separate acknowledge/acknowledgeEx. */
return ::pscPmModuleAcknowledge(std::addressof(this->module), static_cast<::PscPmState>(0));
}
virtual Result Finalize() override final {
return ::pscPmModuleFinalize(std::addressof(this->module));
}
virtual Result AcknowledgeEx(PmState state) override final {
static_assert(sizeof(state) == sizeof(::PscPmState));
return ::pscPmModuleAcknowledge(std::addressof(this->module), static_cast<::PscPmState>(state));
}
};
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "settings_firmware_version_impl.hpp"
namespace ams::settings::impl {
Result GetFirmwareVersion(settings::system::FirmwareVersion *out) {
static_assert(sizeof(*out) == sizeof(::SetSysFirmwareVersion));
return ::setsysGetFirmwareVersion(reinterpret_cast<::SetSysFirmwareVersion *>(out));
}
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::settings::impl {
Result GetFirmwareVersion(settings::system::FirmwareVersion *out);
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "settings_product_model_impl.hpp"
namespace ams::settings::impl {
Result GetProductModel(s32 *out) {
return ::setsysGetProductModel(out);
}
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::settings::impl {
Result GetProductModel(s32 *out);
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "settings_region_impl.hpp"
namespace ams::settings::impl {
Result GetRegionCode(s32 *out) {
static_assert(sizeof(*out) == sizeof(::SetRegion));
return ::setGetRegionCode(reinterpret_cast<::SetRegion *>(out));
}
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::settings::impl {
Result GetRegionCode(s32 *out);
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "settings_serial_number_impl.hpp"
namespace ams::settings::impl {
Result GetSerialNumber(settings::system::SerialNumber *out) {
static_assert(sizeof(*out) == sizeof(::SetSysSerialNumber));
return ::setsysGetSerialNumber(reinterpret_cast<::SetSysSerialNumber *>(out));
}
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::settings::impl {
Result GetSerialNumber(settings::system::SerialNumber *out);
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/settings_firmware_version_impl.hpp"
namespace ams::settings::system {
void GetFirmwareVersion(FirmwareVersion *out) {
R_ABORT_UNLESS(settings::impl::GetFirmwareVersion(out));
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/settings_product_model_impl.hpp"
namespace ams::settings::system {
ProductModel GetProductModel() {
s32 model = 0;
R_ABORT_UNLESS(settings::impl::GetProductModel(std::addressof(model)));
return static_cast<ProductModel>(model);
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/settings_region_impl.hpp"
namespace ams::settings::system {
void GetRegionCode(RegionCode *out) {
AMS_ABORT_UNLESS(out != nullptr);
s32 value = 0;
R_ABORT_UNLESS(settings::impl::GetRegionCode(std::addressof(value)));
*out = static_cast<RegionCode>(value);
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/settings_serial_number_impl.hpp"
namespace ams::settings::system {
void GetSerialNumber(SerialNumber *out) {
R_ABORT_UNLESS(settings::impl::GetSerialNumber(out));
}
}

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::time::impl::util {
Result GetSpanBetween(s64 *out, const SteadyClockTimePoint &from, const SteadyClockTimePoint &to) {
AMS_ASSERT(out != nullptr);
R_UNLESS(out != nullptr, time::ResultInvalidPointer());
R_UNLESS(from.source_id == to.source_id, time::ResultNotComparable());
const bool no_overflow = (from.value >= 0 ? (to.value >= std::numeric_limits<s64>::min() + from.value)
: (to.value <= std::numeric_limits<s64>::max() + from.value));
R_UNLESS(no_overflow, time::ResultOverflowed());
*out = to.value - from.value;
return ResultSuccess();
}
}

View file

@ -0,0 +1,105 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
extern "C" {
extern TimeServiceType __nx_time_service_type;
}
namespace ams::time {
namespace {
enum InitializeMode {
InitializeMode_None,
InitializeMode_Normal,
InitializeMode_Menu,
InitializeMode_System,
InitializeMode_Repair,
InitializeMode_SystemUser,
};
u32 g_initialize_count = 0;
InitializeMode g_initialize_mode = InitializeMode_None;
/* TODO: os::SdkMutex */
os::Mutex g_initialize_mutex(false);
Result InitializeImpl(InitializeMode mode) {
std::scoped_lock lk(g_initialize_mutex);
if (g_initialize_count > 0) {
AMS_ABORT_UNLESS(mode == g_initialize_mode);
g_initialize_count++;
return ResultSuccess();
}
switch (mode) {
case InitializeMode_Normal: __nx_time_service_type = ::TimeServiceType_User; break;
case InitializeMode_Menu: __nx_time_service_type = ::TimeServiceType_Menu; break;
case InitializeMode_System: __nx_time_service_type = ::TimeServiceType_System; break;
case InitializeMode_Repair: __nx_time_service_type = ::TimeServiceType_Repair; break;
case InitializeMode_SystemUser: __nx_time_service_type = ::TimeServiceType_SystemUser; break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
R_TRY(::timeInitialize());
g_initialize_count++;
g_initialize_mode = mode;
return ResultSuccess();
}
}
Result Initialize() {
return InitializeImpl(InitializeMode_Normal);
}
Result InitializeForSystem() {
return InitializeImpl(InitializeMode_System);
}
Result InitializeForSystemUser() {
return InitializeImpl(InitializeMode_System);
}
Result Finalize() {
std::scoped_lock lk(g_initialize_mutex);
if (g_initialize_count > 0) {
if ((--g_initialize_count) == 0) {
::timeExit();
g_initialize_mode = InitializeMode_None;
}
}
return ResultSuccess();
}
bool IsInitialized() {
std::scoped_lock lk(g_initialize_mutex);
return g_initialize_count > 0;
}
Result GetElapsedSecondsBetween(s64 *out, const SteadyClockTimePoint &from, const SteadyClockTimePoint &to) {
return impl::util::GetSpanBetween(out, from, to);
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::time {
Result StandardNetworkSystemClock::GetCurrentTime(PosixTime *out) {
static_assert(sizeof(*out) == sizeof(u64));
return ::timeGetCurrentTime(::TimeType_NetworkSystemClock, reinterpret_cast<u64 *>(out));
}
StandardNetworkSystemClock::time_point StandardNetworkSystemClock::now() {
PosixTime posix_time = {};
if (R_FAILED(GetCurrentTime(std::addressof(posix_time)))) {
posix_time.value = 0;
}
return time_point(duration(posix_time.value));
}
std::time_t StandardNetworkSystemClock::to_time_t(const StandardNetworkSystemClock::time_point &t) {
return static_cast<std::time_t>(std::chrono::duration_cast<std::chrono::seconds>(t.time_since_epoch()).count());
}
StandardNetworkSystemClock::time_point StandardNetworkSystemClock::from_time_t(std::time_t t) {
return time_point(duration(t));
}
/* TODO: Result StandardNetworkSystemClock::GetSystemClockContext(SystemClockContext *out); */
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::time {
Result GetStandardSteadyClockCurrentTimePoint(SteadyClockTimePoint *out) {
static_assert(sizeof(*out) == sizeof(::TimeSteadyClockTimePoint));
return ::timeGetStandardSteadyClockTimePoint(reinterpret_cast<::TimeSteadyClockTimePoint *>(out));
}
TimeSpan GetStandardSteadyClockInternalOffset() {
static_assert(sizeof(TimeSpanType) == sizeof(s64));
TimeSpanType offset;
R_ABORT_UNLESS(::timeGetStandardSteadyClockInternalOffset(reinterpret_cast<s64 *>(std::addressof(offset))));
return offset;
}
Result StandardSteadyClock::GetCurrentTimePoint(SteadyClockTimePoint *out) {
return GetStandardSteadyClockCurrentTimePoint(out);
}
StandardSteadyClock::time_point StandardSteadyClock::now() {
SteadyClockTimePoint steady_clock_time_point = {0, util::InvalidUuid};
if (R_FAILED(StandardSteadyClock::GetCurrentTimePoint(std::addressof(steady_clock_time_point)))) {
steady_clock_time_point.value = 0;
steady_clock_time_point.source_id = util::InvalidUuid;
}
return StandardSteadyClock::time_point(StandardSteadyClock::duration(steady_clock_time_point.value));
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::time {
Result StandardUserSystemClock::GetCurrentTime(PosixTime *out) {
static_assert(sizeof(*out) == sizeof(u64));
return ::timeGetCurrentTime(::TimeType_UserSystemClock, reinterpret_cast<u64 *>(out));
}
StandardUserSystemClock::time_point StandardUserSystemClock::now() {
PosixTime posix_time = {};
if (R_FAILED(GetCurrentTime(std::addressof(posix_time)))) {
posix_time.value = 0;
}
return time_point(duration(posix_time.value));
}
std::time_t StandardUserSystemClock::to_time_t(const StandardUserSystemClock::time_point &t) {
return static_cast<std::time_t>(std::chrono::duration_cast<std::chrono::seconds>(t.time_since_epoch()).count());
}
StandardUserSystemClock::time_point StandardUserSystemClock::from_time_t(std::time_t t) {
return time_point(duration(t));
}
/* TODO: Result StandardUserSystemClock::GetSystemClockContext(SystemClockContext *out); */
}

View file

@ -28,3 +28,5 @@
#include <vapours/crypto/crypto_rsa_pss_sha256_verifier.hpp> #include <vapours/crypto/crypto_rsa_pss_sha256_verifier.hpp>
#include <vapours/crypto/crypto_rsa_oaep_sha256_decoder.hpp> #include <vapours/crypto/crypto_rsa_oaep_sha256_decoder.hpp>
#include <vapours/crypto/crypto_rsa_oaep_sha256_decryptor.hpp> #include <vapours/crypto/crypto_rsa_oaep_sha256_decryptor.hpp>
#include <vapours/crypto/crypto_rsa_oaep_sha256_encryptor.hpp>
#include <vapours/crypto/crypto_csrng.hpp>

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/util.hpp>
namespace ams::crypto {
void GenerateCryptographicallyRandomBytes(void *dst, size_t dst_size);
}

Some files were not shown because too many files have changed in this diff Show more