git subrepo clone https://github.com/Atmosphere-NX/libstratosphere stratosphere/libstratosphere

subrepo:
  subdir:   "stratosphere/libstratosphere"
  merged:   "0c5dab80"
upstream:
  origin:   "https://github.com/Atmosphere-NX/libstratosphere"
  branch:   "master"
  commit:   "0c5dab80"
git-subrepo:
  version:  "0.4.0"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "5d6aba9"
This commit is contained in:
Michael Scire
2019-07-17 20:04:00 -07:00
parent 3ea9f444db
commit f534d3498e
166 changed files with 20474 additions and 0 deletions

View File

@@ -0,0 +1,812 @@
/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */
/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
/* $FreeBSD$ */
/*-
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _SYS_TREE_H_
#define _SYS_TREE_H_
/* FreeBSD <sys/cdefs.h> has a lot of defines we don't really want. */
/* tree.h only actually uses __inline and __unused, so we'll just define those. */
/* #include <sys/cdefs.h> */
#ifndef __inline
#define __inline inline
#endif
#ifndef __unused
#define __unused __attribute__((__unused__))
#endif
/*
* This file defines data structures for different types of trees:
* splay trees and red-black trees.
*
* A splay tree is a self-organizing data structure. Every operation
* on the tree causes a splay to happen. The splay moves the requested
* node to the root of the tree and partly rebalances it.
*
* This has the benefit that request locality causes faster lookups as
* the requested nodes move to the top of the tree. On the other hand,
* every lookup causes memory writes.
*
* The Balance Theorem bounds the total access time for m operations
* and n inserts on an initially empty tree as O((m + n)lg n). The
* amortized cost for a sequence of m accesses to a splay tree is O(lg n);
*
* A red-black tree is a binary search tree with the node color as an
* extra attribute. It fulfills a set of conditions:
* - every search path from the root to a leaf consists of the
* same number of black nodes,
* - each red node (except for the root) has a black parent,
* - each leaf node is black.
*
* Every operation on a red-black tree is bounded as O(lg n).
* The maximum height of a red-black tree is 2lg (n+1).
*/
#define SPLAY_HEAD(name, type) \
struct name { \
struct type *sph_root; /* root of the tree */ \
}
#define SPLAY_INITIALIZER(root) \
{ NULL }
#define SPLAY_INIT(root) do { \
(root)->sph_root = NULL; \
} while (/*CONSTCOND*/ 0)
#define SPLAY_ENTRY(type) \
struct { \
struct type *spe_left; /* left element */ \
struct type *spe_right; /* right element */ \
}
#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
#define SPLAY_ROOT(head) (head)->sph_root
#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
(head)->sph_root = tmp; \
} while (/*CONSTCOND*/ 0)
#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
(head)->sph_root = tmp; \
} while (/*CONSTCOND*/ 0)
#define SPLAY_LINKLEFT(head, tmp, field) do { \
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
tmp = (head)->sph_root; \
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
} while (/*CONSTCOND*/ 0)
#define SPLAY_LINKRIGHT(head, tmp, field) do { \
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
tmp = (head)->sph_root; \
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
} while (/*CONSTCOND*/ 0)
#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
} while (/*CONSTCOND*/ 0)
/* Generates prototypes and inline functions */
#define SPLAY_PROTOTYPE(name, type, field, cmp) \
void name##_SPLAY(struct name *, struct type *); \
void name##_SPLAY_MINMAX(struct name *, int); \
struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
\
/* Finds the node with the same key as elm */ \
static __inline struct type * \
name##_SPLAY_FIND(struct name *head, struct type *elm) \
{ \
if (SPLAY_EMPTY(head)) \
return(NULL); \
name##_SPLAY(head, elm); \
if ((cmp)(elm, (head)->sph_root) == 0) \
return (head->sph_root); \
return (NULL); \
} \
\
static __inline struct type * \
name##_SPLAY_NEXT(struct name *head, struct type *elm) \
{ \
name##_SPLAY(head, elm); \
if (SPLAY_RIGHT(elm, field) != NULL) { \
elm = SPLAY_RIGHT(elm, field); \
while (SPLAY_LEFT(elm, field) != NULL) { \
elm = SPLAY_LEFT(elm, field); \
} \
} else \
elm = NULL; \
return (elm); \
} \
\
static __inline struct type * \
name##_SPLAY_MIN_MAX(struct name *head, int val) \
{ \
name##_SPLAY_MINMAX(head, val); \
return (SPLAY_ROOT(head)); \
}
/* Main splay operation.
* Moves node close to the key of elm to top
*/
#define SPLAY_GENERATE(name, type, field, cmp) \
struct type * \
name##_SPLAY_INSERT(struct name *head, struct type *elm) \
{ \
if (SPLAY_EMPTY(head)) { \
SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
} else { \
int __comp; \
name##_SPLAY(head, elm); \
__comp = (cmp)(elm, (head)->sph_root); \
if(__comp < 0) { \
SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
SPLAY_RIGHT(elm, field) = (head)->sph_root; \
SPLAY_LEFT((head)->sph_root, field) = NULL; \
} else if (__comp > 0) { \
SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
SPLAY_LEFT(elm, field) = (head)->sph_root; \
SPLAY_RIGHT((head)->sph_root, field) = NULL; \
} else \
return ((head)->sph_root); \
} \
(head)->sph_root = (elm); \
return (NULL); \
} \
\
struct type * \
name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
{ \
struct type *__tmp; \
if (SPLAY_EMPTY(head)) \
return (NULL); \
name##_SPLAY(head, elm); \
if ((cmp)(elm, (head)->sph_root) == 0) { \
if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
} else { \
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
name##_SPLAY(head, elm); \
SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
} \
return (elm); \
} \
return (NULL); \
} \
\
void \
name##_SPLAY(struct name *head, struct type *elm) \
{ \
struct type __node, *__left, *__right, *__tmp; \
int __comp; \
\
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
__left = __right = &__node; \
\
while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \
if (__comp < 0) { \
__tmp = SPLAY_LEFT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if ((cmp)(elm, __tmp) < 0){ \
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKLEFT(head, __right, field); \
} else if (__comp > 0) { \
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if ((cmp)(elm, __tmp) > 0){ \
SPLAY_ROTATE_LEFT(head, __tmp, field); \
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKRIGHT(head, __left, field); \
} \
} \
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
} \
\
/* Splay with either the minimum or the maximum element \
* Used to find minimum or maximum element in tree. \
*/ \
void name##_SPLAY_MINMAX(struct name *head, int __comp) \
{ \
struct type __node, *__left, *__right, *__tmp; \
\
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
__left = __right = &__node; \
\
while (1) { \
if (__comp < 0) { \
__tmp = SPLAY_LEFT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if (__comp < 0){ \
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKLEFT(head, __right, field); \
} else if (__comp > 0) { \
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if (__comp > 0) { \
SPLAY_ROTATE_LEFT(head, __tmp, field); \
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKRIGHT(head, __left, field); \
} \
} \
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
}
#define SPLAY_NEGINF -1
#define SPLAY_INF 1
#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
#define SPLAY_FOREACH(x, name, head) \
for ((x) = SPLAY_MIN(name, head); \
(x) != NULL; \
(x) = SPLAY_NEXT(name, head, x))
/* Macros that define a red-black tree */
#define RB_HEAD(name, type) \
struct name { \
struct type *rbh_root; /* root of the tree */ \
}
#define RB_INITIALIZER(root) \
{ NULL }
#define RB_INIT(root) do { \
(root)->rbh_root = NULL; \
} while (/*CONSTCOND*/ 0)
#define RB_BLACK 0
#define RB_RED 1
#define RB_ENTRY(type) \
struct { \
struct type *rbe_left; /* left element */ \
struct type *rbe_right; /* right element */ \
struct type *rbe_parent; /* parent element */ \
int rbe_color; /* node color */ \
}
#define RB_LEFT(elm, field) (elm)->field.rbe_left
#define RB_RIGHT(elm, field) (elm)->field.rbe_right
#define RB_PARENT(elm, field) (elm)->field.rbe_parent
#define RB_COLOR(elm, field) (elm)->field.rbe_color
#define RB_ROOT(head) (head)->rbh_root
#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
#define RB_SET(elm, parent, field) do { \
RB_PARENT(elm, field) = parent; \
RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
RB_COLOR(elm, field) = RB_RED; \
} while (/*CONSTCOND*/ 0)
#define RB_SET_BLACKRED(black, red, field) do { \
RB_COLOR(black, field) = RB_BLACK; \
RB_COLOR(red, field) = RB_RED; \
} while (/*CONSTCOND*/ 0)
#ifndef RB_AUGMENT
#define RB_AUGMENT(x) do {} while (0)
#endif
#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \
(tmp) = RB_RIGHT(elm, field); \
if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \
RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \
} \
RB_AUGMENT(elm); \
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
else \
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
} else \
(head)->rbh_root = (tmp); \
RB_LEFT(tmp, field) = (elm); \
RB_PARENT(elm, field) = (tmp); \
RB_AUGMENT(tmp); \
if ((RB_PARENT(tmp, field))) \
RB_AUGMENT(RB_PARENT(tmp, field)); \
} while (/*CONSTCOND*/ 0)
#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \
(tmp) = RB_LEFT(elm, field); \
if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \
RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \
} \
RB_AUGMENT(elm); \
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
else \
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
} else \
(head)->rbh_root = (tmp); \
RB_RIGHT(tmp, field) = (elm); \
RB_PARENT(elm, field) = (tmp); \
RB_AUGMENT(tmp); \
if ((RB_PARENT(tmp, field))) \
RB_AUGMENT(RB_PARENT(tmp, field)); \
} while (/*CONSTCOND*/ 0)
/* Generates prototypes and inline functions */
#define RB_PROTOTYPE(name, type, field, cmp) \
RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \
RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static)
#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
RB_PROTOTYPE_INSERT_COLOR(name, type, attr); \
RB_PROTOTYPE_REMOVE_COLOR(name, type, attr); \
RB_PROTOTYPE_INSERT(name, type, attr); \
RB_PROTOTYPE_REMOVE(name, type, attr); \
RB_PROTOTYPE_FIND(name, type, attr); \
RB_PROTOTYPE_NFIND(name, type, attr); \
RB_PROTOTYPE_NEXT(name, type, attr); \
RB_PROTOTYPE_PREV(name, type, attr); \
RB_PROTOTYPE_MINMAX(name, type, attr);
#define RB_PROTOTYPE_INSERT_COLOR(name, type, attr) \
attr void name##_RB_INSERT_COLOR(struct name *, struct type *)
#define RB_PROTOTYPE_REMOVE_COLOR(name, type, attr) \
attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *)
#define RB_PROTOTYPE_REMOVE(name, type, attr) \
attr struct type *name##_RB_REMOVE(struct name *, struct type *)
#define RB_PROTOTYPE_INSERT(name, type, attr) \
attr struct type *name##_RB_INSERT(struct name *, struct type *)
#define RB_PROTOTYPE_FIND(name, type, attr) \
attr struct type *name##_RB_FIND(struct name *, struct type *)
#define RB_PROTOTYPE_NFIND(name, type, attr) \
attr struct type *name##_RB_NFIND(struct name *, struct type *)
#define RB_PROTOTYPE_NEXT(name, type, attr) \
attr struct type *name##_RB_NEXT(struct type *)
#define RB_PROTOTYPE_PREV(name, type, attr) \
attr struct type *name##_RB_PREV(struct type *)
#define RB_PROTOTYPE_MINMAX(name, type, attr) \
attr struct type *name##_RB_MINMAX(struct name *, int)
/* Main rb operation.
* Moves node close to the key of elm to top
*/
#define RB_GENERATE(name, type, field, cmp) \
RB_GENERATE_INTERNAL(name, type, field, cmp,)
#define RB_GENERATE_STATIC(name, type, field, cmp) \
RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static)
#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
RB_GENERATE_INSERT_COLOR(name, type, field, attr) \
RB_GENERATE_REMOVE_COLOR(name, type, field, attr) \
RB_GENERATE_INSERT(name, type, field, cmp, attr) \
RB_GENERATE_REMOVE(name, type, field, attr) \
RB_GENERATE_FIND(name, type, field, cmp, attr) \
RB_GENERATE_NFIND(name, type, field, cmp, attr) \
RB_GENERATE_NEXT(name, type, field, attr) \
RB_GENERATE_PREV(name, type, field, attr) \
RB_GENERATE_MINMAX(name, type, field, attr)
#define RB_GENERATE_INSERT_COLOR(name, type, field, attr) \
attr void \
name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
{ \
struct type *parent, *gparent, *tmp; \
while ((parent = RB_PARENT(elm, field)) != NULL && \
RB_COLOR(parent, field) == RB_RED) { \
gparent = RB_PARENT(parent, field); \
if (parent == RB_LEFT(gparent, field)) { \
tmp = RB_RIGHT(gparent, field); \
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
RB_COLOR(tmp, field) = RB_BLACK; \
RB_SET_BLACKRED(parent, gparent, field);\
elm = gparent; \
continue; \
} \
if (RB_RIGHT(parent, field) == elm) { \
RB_ROTATE_LEFT(head, parent, tmp, field);\
tmp = parent; \
parent = elm; \
elm = tmp; \
} \
RB_SET_BLACKRED(parent, gparent, field); \
RB_ROTATE_RIGHT(head, gparent, tmp, field); \
} else { \
tmp = RB_LEFT(gparent, field); \
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
RB_COLOR(tmp, field) = RB_BLACK; \
RB_SET_BLACKRED(parent, gparent, field);\
elm = gparent; \
continue; \
} \
if (RB_LEFT(parent, field) == elm) { \
RB_ROTATE_RIGHT(head, parent, tmp, field);\
tmp = parent; \
parent = elm; \
elm = tmp; \
} \
RB_SET_BLACKRED(parent, gparent, field); \
RB_ROTATE_LEFT(head, gparent, tmp, field); \
} \
} \
RB_COLOR(head->rbh_root, field) = RB_BLACK; \
}
#define RB_GENERATE_REMOVE_COLOR(name, type, field, attr) \
attr void \
name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
{ \
struct type *tmp; \
while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \
elm != RB_ROOT(head)) { \
if (RB_LEFT(parent, field) == elm) { \
tmp = RB_RIGHT(parent, field); \
if (RB_COLOR(tmp, field) == RB_RED) { \
RB_SET_BLACKRED(tmp, parent, field); \
RB_ROTATE_LEFT(head, parent, tmp, field);\
tmp = RB_RIGHT(parent, field); \
} \
if ((RB_LEFT(tmp, field) == NULL || \
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
(RB_RIGHT(tmp, field) == NULL || \
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
RB_COLOR(tmp, field) = RB_RED; \
elm = parent; \
parent = RB_PARENT(elm, field); \
} else { \
if (RB_RIGHT(tmp, field) == NULL || \
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
struct type *oleft; \
if ((oleft = RB_LEFT(tmp, field)) \
!= NULL) \
RB_COLOR(oleft, field) = RB_BLACK;\
RB_COLOR(tmp, field) = RB_RED; \
RB_ROTATE_RIGHT(head, tmp, oleft, field);\
tmp = RB_RIGHT(parent, field); \
} \
RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
RB_COLOR(parent, field) = RB_BLACK; \
if (RB_RIGHT(tmp, field)) \
RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
RB_ROTATE_LEFT(head, parent, tmp, field);\
elm = RB_ROOT(head); \
break; \
} \
} else { \
tmp = RB_LEFT(parent, field); \
if (RB_COLOR(tmp, field) == RB_RED) { \
RB_SET_BLACKRED(tmp, parent, field); \
RB_ROTATE_RIGHT(head, parent, tmp, field);\
tmp = RB_LEFT(parent, field); \
} \
if ((RB_LEFT(tmp, field) == NULL || \
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
(RB_RIGHT(tmp, field) == NULL || \
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
RB_COLOR(tmp, field) = RB_RED; \
elm = parent; \
parent = RB_PARENT(elm, field); \
} else { \
if (RB_LEFT(tmp, field) == NULL || \
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
struct type *oright; \
if ((oright = RB_RIGHT(tmp, field)) \
!= NULL) \
RB_COLOR(oright, field) = RB_BLACK;\
RB_COLOR(tmp, field) = RB_RED; \
RB_ROTATE_LEFT(head, tmp, oright, field);\
tmp = RB_LEFT(parent, field); \
} \
RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
RB_COLOR(parent, field) = RB_BLACK; \
if (RB_LEFT(tmp, field)) \
RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
RB_ROTATE_RIGHT(head, parent, tmp, field);\
elm = RB_ROOT(head); \
break; \
} \
} \
} \
if (elm) \
RB_COLOR(elm, field) = RB_BLACK; \
}
#define RB_GENERATE_REMOVE(name, type, field, attr) \
attr struct type * \
name##_RB_REMOVE(struct name *head, struct type *elm) \
{ \
struct type *child, *parent, *old = elm; \
int color; \
if (RB_LEFT(elm, field) == NULL) \
child = RB_RIGHT(elm, field); \
else if (RB_RIGHT(elm, field) == NULL) \
child = RB_LEFT(elm, field); \
else { \
struct type *left; \
elm = RB_RIGHT(elm, field); \
while ((left = RB_LEFT(elm, field)) != NULL) \
elm = left; \
child = RB_RIGHT(elm, field); \
parent = RB_PARENT(elm, field); \
color = RB_COLOR(elm, field); \
if (child) \
RB_PARENT(child, field) = parent; \
if (parent) { \
if (RB_LEFT(parent, field) == elm) \
RB_LEFT(parent, field) = child; \
else \
RB_RIGHT(parent, field) = child; \
RB_AUGMENT(parent); \
} else \
RB_ROOT(head) = child; \
if (RB_PARENT(elm, field) == old) \
parent = elm; \
(elm)->field = (old)->field; \
if (RB_PARENT(old, field)) { \
if (RB_LEFT(RB_PARENT(old, field), field) == old)\
RB_LEFT(RB_PARENT(old, field), field) = elm;\
else \
RB_RIGHT(RB_PARENT(old, field), field) = elm;\
RB_AUGMENT(RB_PARENT(old, field)); \
} else \
RB_ROOT(head) = elm; \
RB_PARENT(RB_LEFT(old, field), field) = elm; \
if (RB_RIGHT(old, field)) \
RB_PARENT(RB_RIGHT(old, field), field) = elm; \
if (parent) { \
left = parent; \
do { \
RB_AUGMENT(left); \
} while ((left = RB_PARENT(left, field)) != NULL); \
} \
goto color; \
} \
parent = RB_PARENT(elm, field); \
color = RB_COLOR(elm, field); \
if (child) \
RB_PARENT(child, field) = parent; \
if (parent) { \
if (RB_LEFT(parent, field) == elm) \
RB_LEFT(parent, field) = child; \
else \
RB_RIGHT(parent, field) = child; \
RB_AUGMENT(parent); \
} else \
RB_ROOT(head) = child; \
color: \
if (color == RB_BLACK) \
name##_RB_REMOVE_COLOR(head, parent, child); \
return (old); \
} \
#define RB_GENERATE_INSERT(name, type, field, cmp, attr) \
/* Inserts a node into the RB tree */ \
attr struct type * \
name##_RB_INSERT(struct name *head, struct type *elm) \
{ \
struct type *tmp; \
struct type *parent = NULL; \
int comp = 0; \
tmp = RB_ROOT(head); \
while (tmp) { \
parent = tmp; \
comp = (cmp)(elm, parent); \
if (comp < 0) \
tmp = RB_LEFT(tmp, field); \
else if (comp > 0) \
tmp = RB_RIGHT(tmp, field); \
else \
return (tmp); \
} \
RB_SET(elm, parent, field); \
if (parent != NULL) { \
if (comp < 0) \
RB_LEFT(parent, field) = elm; \
else \
RB_RIGHT(parent, field) = elm; \
RB_AUGMENT(parent); \
} else \
RB_ROOT(head) = elm; \
name##_RB_INSERT_COLOR(head, elm); \
return (NULL); \
}
#define RB_GENERATE_FIND(name, type, field, cmp, attr) \
/* Finds the node with the same key as elm */ \
attr struct type * \
name##_RB_FIND(struct name *head, struct type *elm) \
{ \
struct type *tmp = RB_ROOT(head); \
int comp; \
while (tmp) { \
comp = cmp(elm, tmp); \
if (comp < 0) \
tmp = RB_LEFT(tmp, field); \
else if (comp > 0) \
tmp = RB_RIGHT(tmp, field); \
else \
return (tmp); \
} \
return (NULL); \
}
#define RB_GENERATE_NFIND(name, type, field, cmp, attr) \
/* Finds the first node greater than or equal to the search key */ \
attr struct type * \
name##_RB_NFIND(struct name *head, struct type *elm) \
{ \
struct type *tmp = RB_ROOT(head); \
struct type *res = NULL; \
int comp; \
while (tmp) { \
comp = cmp(elm, tmp); \
if (comp < 0) { \
res = tmp; \
tmp = RB_LEFT(tmp, field); \
} \
else if (comp > 0) \
tmp = RB_RIGHT(tmp, field); \
else \
return (tmp); \
} \
return (res); \
}
#define RB_GENERATE_NEXT(name, type, field, attr) \
/* ARGSUSED */ \
attr struct type * \
name##_RB_NEXT(struct type *elm) \
{ \
if (RB_RIGHT(elm, field)) { \
elm = RB_RIGHT(elm, field); \
while (RB_LEFT(elm, field)) \
elm = RB_LEFT(elm, field); \
} else { \
if (RB_PARENT(elm, field) && \
(elm == RB_LEFT(RB_PARENT(elm, field), field))) \
elm = RB_PARENT(elm, field); \
else { \
while (RB_PARENT(elm, field) && \
(elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
elm = RB_PARENT(elm, field); \
elm = RB_PARENT(elm, field); \
} \
} \
return (elm); \
}
#define RB_GENERATE_PREV(name, type, field, attr) \
/* ARGSUSED */ \
attr struct type * \
name##_RB_PREV(struct type *elm) \
{ \
if (RB_LEFT(elm, field)) { \
elm = RB_LEFT(elm, field); \
while (RB_RIGHT(elm, field)) \
elm = RB_RIGHT(elm, field); \
} else { \
if (RB_PARENT(elm, field) && \
(elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
elm = RB_PARENT(elm, field); \
else { \
while (RB_PARENT(elm, field) && \
(elm == RB_LEFT(RB_PARENT(elm, field), field)))\
elm = RB_PARENT(elm, field); \
elm = RB_PARENT(elm, field); \
} \
} \
return (elm); \
}
#define RB_GENERATE_MINMAX(name, type, field, attr) \
attr struct type * \
name##_RB_MINMAX(struct name *head, int val) \
{ \
struct type *tmp = RB_ROOT(head); \
struct type *parent = NULL; \
while (tmp) { \
parent = tmp; \
if (val < 0) \
tmp = RB_LEFT(tmp, field); \
else \
tmp = RB_RIGHT(tmp, field); \
} \
return (parent); \
}
#define RB_NEGINF -1
#define RB_INF 1
#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y)
#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
#define RB_PREV(name, x, y) name##_RB_PREV(y)
#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
#define RB_FOREACH(x, name, head) \
for ((x) = RB_MIN(name, head); \
(x) != NULL; \
(x) = name##_RB_NEXT(x))
#define RB_FOREACH_FROM(x, name, y) \
for ((x) = (y); \
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
(x) = (y))
#define RB_FOREACH_SAFE(x, name, head, y) \
for ((x) = RB_MIN(name, head); \
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
(x) = (y))
#define RB_FOREACH_REVERSE(x, name, head) \
for ((x) = RB_MAX(name, head); \
(x) != NULL; \
(x) = name##_RB_PREV(x))
#define RB_FOREACH_REVERSE_FROM(x, name, y) \
for ((x) = (y); \
((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
(x) = (y))
#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \
for ((x) = RB_MAX(name, head); \
((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
(x) = (y))
#endif /* _SYS_TREE_H_ */

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2018-2019 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/defines.hpp"
#include "stratosphere/utilities.hpp"
#include "stratosphere/emummc_utilities.hpp"
#include "stratosphere/scope_guard.hpp"
#include "stratosphere/version_check.hpp"
#include "stratosphere/auto_handle.hpp"
#include "stratosphere/hossynch.hpp"
#include "stratosphere/message_queue.hpp"
#include "stratosphere/iwaitable.hpp"
#include "stratosphere/event.hpp"
#include "stratosphere/waitable_manager.hpp"
#include "stratosphere/ipc.hpp"
#include "stratosphere/mitm.hpp"
#include "stratosphere/services.hpp"
#include "stratosphere/results.hpp"
#include "stratosphere/on_crash.hpp"
#include "stratosphere/svc.hpp"
#include "stratosphere/cfg.hpp"
#include "stratosphere/hid.hpp"
#include "stratosphere/ncm.hpp"
#include "stratosphere/pm.hpp"
#include "stratosphere/rnd.hpp"
#include "stratosphere/sm.hpp"
#include "stratosphere/util.hpp"

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "defines.hpp"
class AutoHandle {
NON_COPYABLE(AutoHandle);
private:
Handle hnd;
public:
AutoHandle() : hnd(INVALID_HANDLE) { /* ... */ }
AutoHandle(Handle h) : hnd(h) { /* ... */ }
~AutoHandle() {
if (this->hnd != INVALID_HANDLE) {
svcCloseHandle(this->hnd);
this->hnd = INVALID_HANDLE;
}
}
AutoHandle(AutoHandle&& rhs) {
this->hnd = rhs.hnd;
rhs.hnd = INVALID_HANDLE;
}
AutoHandle& operator=(AutoHandle&& rhs) {
rhs.Swap(*this);
return *this;
}
explicit operator bool() const {
return this->hnd != INVALID_HANDLE;
}
void Swap(AutoHandle& rhs) {
std::swap(this->hnd, rhs.hnd);
}
Handle Get() const {
return this->hnd;
}
Handle *GetPointer() {
return &this->hnd;
}
Handle *GetPointerAndClear() {
this->Clear();
return this->GetPointer();
}
Handle Move() {
const Handle h = this->hnd;
this->hnd = INVALID_HANDLE;
return h;
}
void Reset(Handle h) {
AutoHandle(h).Swap(*this);
}
void Clear() {
this->Reset(INVALID_HANDLE);
}
};

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "cfg/cfg_api.hpp"

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "../ncm/ncm_types.hpp"
namespace sts::cfg {
/* Privileged Process configuration. */
bool IsInitialProcess();
void GetInitialProcessRange(u64 *out_min, u64 *out_max);
/* SD card configuration. */
bool IsSdCardInitialized();
void WaitSdCardInitialized();
/* Override key utilities. */
bool IsTitleOverrideKeyHeld(ncm::TitleId title_id);
bool IsHblOverrideKeyHeld(ncm::TitleId title_id);
void GetOverrideKeyHeldStatus(bool *out_hbl, bool *out_title, ncm::TitleId title_id);
bool IsCheatEnableKeyHeld(ncm::TitleId title_id);
/* Flag utilities. */
bool HasFlag(ncm::TitleId title_id, const char *flag);
bool HasTitleSpecificFlag(ncm::TitleId title_id, const char *flag);
bool HasGlobalFlag(const char *flag);
/* HBL Configuration utilities. */
bool IsHblTitleId(ncm::TitleId title_id);
bool HasHblFlag(const char *flag);
const char *GetHblPath();
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2018-2019 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 <iterator>
/* Any broadly useful language defines should go here. */
#define NON_COPYABLE(cls) \
cls(const cls&) = delete; \
cls& operator=(const cls&) = delete
#define NON_MOVEABLE(cls) \
cls(cls&&) = delete; \
cls& operator=(cls&&) = delete
#define ALIGNED(algn) __attribute__((aligned(algn)))
#define WEAK __attribute__((weak))
namespace sts::util {
/* std::size() does not support zero-size C arrays. We're fixing that. */
template<class C>
constexpr auto size(const C& c) -> decltype(c.size()) {
return std::size(c);
}
template<class C>
constexpr std::size_t size(const C& c) {
if constexpr (sizeof(C) == 0) {
return 0;
} else {
return std::size(c);
}
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include <cstdlib>
/* Get whether emummc is active. */
bool IsEmummc();
/* Get Nintendo redirection path. */
const char *GetEmummcNintendoDirPath();
/* Get Emummc folderpath, NULL if not file-based. */
const char *GetEmummcFilePath();

View File

@@ -0,0 +1,139 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include <algorithm>
#include <vector>
#include "iwaitable.hpp"
#include "results.hpp"
class IEvent : public IWaitable {
public:
/* Information members. */
Handle r_h;
Handle w_h;
bool autoclear;
public:
IEvent(bool a = false) : r_h(INVALID_HANDLE), w_h(INVALID_HANDLE), autoclear(a) { }
IEvent(Handle r, bool a = false) : r_h(r), w_h(INVALID_HANDLE), autoclear(a) { }
IEvent(Handle r, Handle w, bool a = false) : r_h(r), w_h(w), autoclear(a) { }
~IEvent() {
if (r_h != INVALID_HANDLE) {
svcCloseHandle(r_h);
}
if (w_h != INVALID_HANDLE) {
svcCloseHandle(w_h);
}
}
/* Make it non-copyable */
IEvent() = delete;
IEvent(const IEvent &) = delete;
IEvent& operator=(const IEvent&) = delete;
bool IsAutoClear() {
return this->autoclear;
}
void Clear() {
std::scoped_lock<HosMutex> lock(this->sig_lock);
this->is_signaled = false;
if (this->w_h != INVALID_HANDLE) {
svcClearEvent(this->w_h);
} else if (this->r_h != INVALID_HANDLE) {
svcResetSignal(this->r_h);
}
}
void Signal() {
std::scoped_lock<HosMutex> lock(this->sig_lock);
if (this->w_h == INVALID_HANDLE && this->r_h != INVALID_HANDLE) {
/* We can't signal an event if we only have a read handle. */
std::abort();
}
if (this->w_h == INVALID_HANDLE && this->is_signaled) {
return;
}
this->is_signaled = true;
if (this->w_h != INVALID_HANDLE) {
svcSignalEvent(this->w_h);
} else {
this->NotifyManagerSignaled();
}
}
virtual Result HandleSignaled(u64 timeout) = 0;
/* IWaitable */
virtual Handle GetHandle() override {
return this->r_h;
}
};
template<class F>
class HosEvent : public IEvent {
private:
F callback;
public:
HosEvent(F f, bool a = false) : IEvent(a), callback(std::move(f)) { }
HosEvent(Handle r, F f, bool a = false) : IEvent(r, a), callback(std::move(f)) { }
HosEvent(Handle r, Handle w, F f, bool a = false) : IEvent(r, w, a), callback(std::move(f)) { }
virtual Result HandleSignaled(u64 timeout) override {
if (this->IsAutoClear()) {
this->Clear();
}
return this->callback(timeout);
}
};
template <class F>
static IEvent *CreateHosEvent(F f, bool autoclear = false) {
return new HosEvent<F>(INVALID_HANDLE, INVALID_HANDLE, std::move(f), autoclear);
}
template <class F>
static IEvent *CreateSystemEvent(F f, bool autoclear = false) {
Handle w_h, r_h;
R_ASSERT(svcCreateEvent(&w_h, &r_h));
return new HosEvent<F>(r_h, w_h, std::move(f), autoclear);
}
template <class F>
static IEvent *CreateInterruptEvent(F f, u64 irq, bool autoclear = false) {
Handle r_h;
/* flag is "rising edge vs level". */
R_ASSERT(svcCreateInterruptEvent(&r_h, irq, autoclear ? 0 : 1));
return new HosEvent<F>(r_h, INVALID_HANDLE, std::move(f), autoclear);
}
template <bool a = false>
static IEvent *CreateWriteOnlySystemEvent() {
return CreateSystemEvent([](u64 timeout) -> Result { std::abort(); }, a);
}
template <class F>
static IEvent *LoadReadOnlySystemEvent(Handle r_h, F f, bool autoclear = false) {
return new HosEvent<F>(r_h, f, autoclear);
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2018-2019 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
enum FirmwareVersion : u32 {
FirmwareVersion_Min = 0,
FirmwareVersion_100 = FirmwareVersion_Min,
FirmwareVersion_200 = 1,
FirmwareVersion_300 = 2,
FirmwareVersion_400 = 3,
FirmwareVersion_500 = 4,
FirmwareVersion_600 = 5,
FirmwareVersion_700 = 6,
FirmwareVersion_800 = 7,
FirmwareVersion_810 = 8,
FirmwareVersion_Current = FirmwareVersion_810,
FirmwareVersion_Max = 32,
};
enum AtmosphereTargetFirmware : u32 {
AtmosphereTargetFirmware_100 = 1,
AtmosphereTargetFirmware_200 = 2,
AtmosphereTargetFirmware_300 = 3,
AtmosphereTargetFirmware_400 = 4,
AtmosphereTargetFirmware_500 = 5,
AtmosphereTargetFirmware_600 = 6,
AtmosphereTargetFirmware_620 = 7,
AtmosphereTargetFirmware_700 = 8,
AtmosphereTargetFirmware_800 = 9,
AtmosphereTargetFirmware_810 = 10,
};
FirmwareVersion GetRuntimeFirmwareVersion();
void SetFirmwareVersionForLibnx();

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "hid/hid_api.hpp"

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2018-2019 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
namespace sts::hid {
/* Key API. */
Result GetKeysHeld(u64 *out);
}

View File

@@ -0,0 +1,286 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include <switch/arm/counter.h>
#include <mutex>
#include "results.hpp"
#include "auto_handle.hpp"
class HosMutex {
private:
Mutex m;
Mutex *GetMutex() {
return &this->m;
}
public:
HosMutex() {
mutexInit(GetMutex());
}
void lock() {
mutexLock(GetMutex());
}
void unlock() {
mutexUnlock(GetMutex());
}
bool try_lock() {
return mutexTryLock(GetMutex());
}
void Lock() {
lock();
}
void Unlock() {
unlock();
}
bool TryLock() {
return try_lock();
}
friend class HosCondVar;
};
class HosRecursiveMutex {
private:
RMutex m;
RMutex *GetMutex() {
return &this->m;
}
public:
HosRecursiveMutex() {
rmutexInit(GetMutex());
}
void lock() {
rmutexLock(GetMutex());
}
void unlock() {
rmutexUnlock(GetMutex());
}
bool try_lock() {
return rmutexTryLock(GetMutex());
}
void Lock() {
lock();
}
void Unlock() {
unlock();
}
bool TryLock() {
return try_lock();
}
};
class HosCondVar {
private:
CondVar cv;
public:
HosCondVar() {
condvarInit(&cv);
}
Result TimedWait(u64 timeout, HosMutex *hm) {
return TimedWait(timeout, hm->GetMutex());
}
Result Wait(HosMutex *hm) {
return Wait(hm->GetMutex());
}
Result TimedWait(u64 timeout, Mutex *m) {
return condvarWaitTimeout(&cv, m, timeout);
}
Result Wait(Mutex *m) {
return condvarWait(&cv, m);
}
Result Wake(int num) {
return condvarWake(&cv, num);
}
Result WakeOne() {
return condvarWakeOne(&cv);
}
Result WakeAll() {
return condvarWakeAll(&cv);
}
};
class HosSemaphore {
private:
Semaphore s;
public:
HosSemaphore() {
semaphoreInit(&s, 0);
}
HosSemaphore(u64 c) {
semaphoreInit(&s, c);
}
void Signal() {
semaphoreSignal(&s);
}
void Wait() {
semaphoreWait(&s);
}
bool TryWait() {
return semaphoreTryWait(&s);
}
};
class TimeoutHelper {
private:
u64 end_tick;
public:
TimeoutHelper(u64 ns) {
/* Special case zero-time timeouts. */
if (ns == 0) {
end_tick = 0;
return;
}
u64 cur_tick = armGetSystemTick();
this->end_tick = cur_tick + NsToTick(ns) + 1;
}
static inline u64 NsToTick(u64 ns) {
return (ns * 12) / 625;
}
static inline u64 TickToNs(u64 tick) {
return (tick * 625) / 12;
}
u64 NsUntilTimeout() {
u64 diff = TickToNs(this->end_tick - armGetSystemTick());
if (TimedOut()) {
return 0;
}
return diff;
}
bool TimedOut() {
if (this->end_tick == 0) {
return true;
}
return armGetSystemTick() >= this->end_tick;
}
};
class HosSignal {
private:
CondVar cv;
Mutex m;
bool signaled;
public:
HosSignal() {
condvarInit(&cv);
mutexInit(&m);
signaled = false;
}
void Signal() {
mutexLock(&m);
signaled = true;
condvarWakeAll(&cv);
mutexUnlock(&m);
}
void Reset() {
mutexLock(&m);
signaled = false;
mutexUnlock(&m);
}
void Wait() {
mutexLock(&m);
while (!signaled) {
condvarWait(&cv, &m);
}
mutexUnlock(&m);
}
bool TryWait() {
mutexLock(&m);
bool success = signaled;
mutexUnlock(&m);
return success;
}
Result TimedWait(u64 ns) {
mutexLock(&m);
TimeoutHelper timeout_helper(ns);
while (!signaled) {
if (R_FAILED(condvarWaitTimeout(&cv, &m, timeout_helper.NsUntilTimeout()))) {
return false;
}
}
mutexUnlock(&m);
return true;
}
};
class HosThread {
private:
Thread thr = {};
public:
HosThread() {}
Result Initialize(ThreadFunc entry, void *arg, size_t stack_sz, int prio, int cpuid = -2) {
return threadCreate(&this->thr, entry, arg, stack_sz, prio, cpuid);
}
Handle GetHandle() const {
return this->thr.handle;
}
Result Start() {
return threadStart(&this->thr);
}
Result Join() {
R_TRY(threadWaitForExit(&this->thr));
R_TRY(threadClose(&this->thr));
return ResultSuccess;
}
Result CancelSynchronization() {
return svcCancelSynchronization(this->thr.handle);
}
};

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) 2018-2019 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 "ipc/ipc_service_object.hpp"
#include "ipc/ipc_serialization.hpp"
#include "ipc/ipc_service_session.hpp"

View File

@@ -0,0 +1,117 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include <type_traits>
enum class IpcBufferType {
InBuffer,
OutBuffer,
InPointer,
OutPointer,
};
/* Base for In/Out Buffers. */
struct IpcBufferBase {};
struct InOutBufferBase : public IpcBufferBase {};
/* Represents an A descriptor. */
struct InBufferBase : public InOutBufferBase {};
template <typename T, BufferType e_t = BufferType_Normal>
struct InBuffer : public InBufferBase {
T *buffer;
size_t num_elements;
BufferType type;
static const BufferType expected_type = e_t;
/* Convenience. */
T& operator[](size_t i) const {
return buffer[i];
}
InBuffer(void *b, size_t n, BufferType t) : buffer((T *)b), num_elements(n/sizeof(T)), type(t) { }
};
/* Represents a B descriptor. */
struct OutBufferBase : public InOutBufferBase {};
template <typename T, BufferType e_t = BufferType_Normal>
struct OutBuffer : OutBufferBase {
T *buffer;
size_t num_elements;
BufferType type;
static const BufferType expected_type = e_t;
/* Convenience. */
T& operator[](size_t i) const {
return buffer[i];
}
OutBuffer(void *b, size_t n, BufferType t) : buffer((T *)b), num_elements(n/sizeof(T)), type(t) { }
};
/* Represents an X descriptor. */
struct InPointerBase : public IpcBufferBase {};
template <typename T>
struct InPointer : public InPointerBase {
T *pointer;
size_t num_elements;
/* Convenience. */
T& operator[](size_t i) const {
return pointer[i];
}
InPointer(void *p, size_t n) : pointer((T *)p), num_elements(n/sizeof(T)) { }
};
/* Represents a C descriptor. */
struct OutPointerWithServerSizeBase : public IpcBufferBase {};
template <typename T, size_t N>
struct OutPointerWithServerSize : public OutPointerWithServerSizeBase {
T *pointer;
static const size_t num_elements = N;
static const size_t element_size = sizeof(T);
/* Convenience. */
T& operator[](size_t i) const {
return pointer[i];
}
OutPointerWithServerSize(void *p) : pointer((T *)p) { }
OutPointerWithServerSize(void *p, size_t n) : pointer((T *)p) { }
};
struct OutPointerWithClientSizeBase : public IpcBufferBase {};
/* Represents a C descriptor with size in raw data. */
template <typename T>
struct OutPointerWithClientSize : public OutPointerWithClientSizeBase {
T *pointer;
size_t num_elements;
/* Convenience. */
T& operator[](size_t i) const {
return pointer[i];
}
OutPointerWithClientSize(void *p, size_t n) : pointer((T *)p), num_elements(n/sizeof(T)) { }
};

View File

@@ -0,0 +1,130 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include <algorithm>
#include <memory>
#include <type_traits>
#include "ipc_service_object.hpp"
class IDomainObject;
class DomainManager {
public:
static constexpr u32 MinimumDomainId = 1;
public:
virtual std::shared_ptr<IDomainObject> AllocateDomain() = 0;
virtual void FreeDomain(IDomainObject *domain) = 0;
virtual Result ReserveObject(IDomainObject *domain, u32 *out_object_id) = 0;
virtual Result ReserveSpecificObject(IDomainObject *domain, u32 object_id) = 0;
virtual void SetObject(IDomainObject *domain, u32 object_id, ServiceObjectHolder&& holder) = 0;
virtual ServiceObjectHolder *GetObject(IDomainObject *domain, u32 object_id) = 0;
virtual Result FreeObject(IDomainObject *domain, u32 object_id) = 0;
virtual Result ForceFreeObject(u32 object_id) = 0;
};
class IDomainObject : public IServiceObject {
private:
DomainManager *manager;
public:
IDomainObject(DomainManager *m) : manager(m) {}
virtual ~IDomainObject() override {
this->manager->FreeDomain(this);
}
DomainManager *GetManager() {
return this->manager;
}
ServiceObjectHolder *GetObject(u32 object_id) {
return this->manager->GetObject(this, object_id);
}
Result ReserveObject(u32 *out_object_id) {
return this->manager->ReserveObject(this, out_object_id);
}
Result ReserveSpecificObject(u32 object_id) {
return this->manager->ReserveSpecificObject(this, object_id);
}
void SetObject(u32 object_id, ServiceObjectHolder&& holder) {
this->manager->SetObject(this, object_id, std::move(holder));
}
Result FreeObject(u32 object_id) {
return this->manager->FreeObject(this, object_id);
}
Result ForceFreeObject(u32 object_id) {
return this->manager->ForceFreeObject(object_id);
}
public:
DEFINE_SERVICE_DISPATCH_TABLE {
/* IDomainObject has no callable functions. */
};
};
static constexpr bool IsDomainObject(ServiceObjectHolder &holder) {
return holder.GetServiceId() == ServiceObjectId<IDomainObject>();
}
static constexpr bool IsDomainObject(ServiceObjectHolder *holder) {
return holder->GetServiceId() == ServiceObjectId<IDomainObject>();
}
/* Out for service impl. */
template <typename ServiceImpl>
class Out<std::shared_ptr<ServiceImpl>> : public OutSessionTag {
static_assert(std::is_base_of_v<IServiceObject, ServiceImpl>, "OutSessions must be shared_ptr<IServiceObject>!");
template<typename, typename>
friend class Out;
private:
std::shared_ptr<ServiceImpl> *srv;
IDomainObject *domain = nullptr;
u32 *object_id = nullptr;
public:
Out<std::shared_ptr<ServiceImpl>>(std::shared_ptr<IServiceObject> *s, IDomainObject *dm, u32 *o) : srv(reinterpret_cast<std::shared_ptr<ServiceImpl> *>(s)), domain(dm), object_id(o) { }
ServiceObjectHolder GetHolder() {
std::shared_ptr<ServiceImpl> clone = *srv;
return ServiceObjectHolder(std::move(clone));
}
bool IsDomain() {
return domain != nullptr;
}
u32 GetObjectId() {
return *object_id;
}
void ChangeObjectId(u32 o) {
domain->ForceFreeObject(*object_id);
domain->ReserveSpecificObject(o);
*object_id = o;
}
void SetValue(std::shared_ptr<ServiceImpl> &&s) {
*this->srv = std::move(s);
}
};

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include <type_traits>
/* Declare false allowed struct. */
template <typename>
struct AllowedOut : std::false_type {};
struct OutDataTag{};
struct OutHandleTag{};
struct OutSessionTag{};
/* Define out struct, so that we can get errors on enable_if */
template <typename T, typename Allowed = void>
class Out {
static_assert(std::is_pod<T>::value && !std::is_pod<T>::value, "Invalid IPC Out Type!");
};
template <typename T>
class Out<T, typename std::enable_if<std::is_trivial<T>::value || AllowedOut<T>::value>::type> : public OutDataTag {
private:
T *obj;
public:
Out(T *o) : obj(o) { }
void SetValue(const T& t) {
*obj = t;
}
const T& GetValue() {
return *obj;
}
T *GetPointer() {
return obj;
}
/* Convenience operators. */
T& operator*() {
return *obj;
}
T* operator->() {
return obj;
}
};
template <typename T>
class Out<T*> {
static_assert(std::is_pod<T>::value && !std::is_pod<T>::value, "Invalid IPC Out Type (Raw Pointer)!");
};
template <typename T>
struct OutHelper;
template <typename T>
struct OutHelper<Out<T>> {
using type = T;
};

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "ipc_service_object.hpp"
#include "ipc_domain_object.hpp"
#include "ipc_special.hpp"
#include "ipc_session_manager_base.hpp"
struct IpcResponseContext {
/* Request/Reply data. */
IpcParsedCommand request;
IpcCommand reply;
u8 out_data[0x100];
std::shared_ptr<IServiceObject> *out_objs[8];
Handle out_object_server_handles[8];
IpcHandle out_handles[8];
u32 out_object_ids[8];
IpcCommandType cmd_type;
u64 cmd_id;
Result rc;
/* Context. */
SessionManagerBase *manager;
ServiceObjectHolder *obj_holder;
unsigned char *pb;
size_t pb_size;
};

View File

@@ -0,0 +1,672 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include <cstdlib>
#include <cstring>
#include <tuple>
#include <type_traits>
#include <memory>
#include "../results.hpp"
#include "ipc_out.hpp"
#include "ipc_buffers.hpp"
#include "ipc_special.hpp"
#include "ipc_domain_object.hpp"
#include "ipc_response_context.hpp"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
template <typename...>
struct TypeList{};
template <typename... T1s, typename... T2s>
constexpr auto Concatenate(TypeList<T1s...>, TypeList<T2s...>) {
return TypeList<T1s..., T2s...>{};
}
template <template <typename> typename Condition, typename R>
constexpr auto FilterTypes(R result, TypeList<>) {
return result;
}
template <template <typename> typename Condition, typename R, typename T, typename... Ts>
constexpr auto FilterTypes(R result, TypeList<T, Ts...>) {
if constexpr (Condition<T>{})
return FilterTypes<Condition>(Concatenate(result, TypeList<T>{}), TypeList<Ts...>{});
else
return FilterTypes<Condition>(result, TypeList<Ts...>{});
}
template<typename Types> struct TypeListToTuple;
template<typename... Types>
struct TypeListToTuple<TypeList<Types...>> {
using type = std::tuple<Types...>;
};
template <template <typename> typename Condition, typename... Types>
using FilteredTypes = typename TypeListToTuple<std::decay_t<decltype(FilterTypes<Condition>(TypeList<>{}, TypeList<Types...>{}))>>::type;
enum class ArgType {
InData,
OutData,
InHandle,
OutHandle,
InSession,
OutSession,
PidDesc,
InBuffer,
OutBuffer,
InPointer,
OutPointerClientSize,
OutPointerServerSize,
};
template<typename X>
constexpr ArgType GetArgType() {
if constexpr (std::is_base_of_v<OutDataTag, X>) {
return ArgType::OutData;
} else if constexpr (std::is_base_of_v<OutSessionTag, X>) {
return ArgType::OutSession;
} else if constexpr (std::is_base_of_v<OutHandleTag, X>) {
return ArgType::OutHandle;
} else if constexpr (std::is_base_of_v<InBufferBase, X>) {
return ArgType::InBuffer;
} else if constexpr (std::is_base_of_v<OutBufferBase, X>) {
return ArgType::OutBuffer;
} else if constexpr (std::is_base_of_v<InPointerBase, X>) {
return ArgType::InPointer;
} else if constexpr (std::is_base_of_v<OutPointerWithClientSizeBase, X>) {
return ArgType::OutPointerClientSize;
} else if constexpr (std::is_base_of_v<OutPointerWithServerSizeBase, X>) {
return ArgType::OutPointerServerSize;
} else if constexpr (std::is_base_of_v<PidDescriptorTag, X>) {
return ArgType::PidDesc;
} else if constexpr (std::is_base_of_v<IpcHandleTag, X>) {
return ArgType::InHandle;
} else if constexpr (std::is_trivial_v<X> && !std::is_pointer_v<X>) {
return ArgType::InData;
} else {
static_assert(std::is_pod_v<X> && !std::is_pod_v<X>, "Unhandled InSession!");
return ArgType::InSession;
}
}
template<ArgType ArgT>
struct ArgTypeFilter {
template<typename X>
using type = std::conditional_t<GetArgType<X>() == ArgT, std::true_type, std::false_type>;
};
template<ArgType ArgT>
struct IsArgTypeBuffer {
static constexpr bool value = ArgT == ArgType::InBuffer || ArgT == ArgType::OutBuffer || ArgT == ArgType::InPointer || ArgT == ArgType::OutPointerClientSize || ArgT == ArgType::OutPointerServerSize;
};
struct ArgTypeBufferFilter {
template<typename X>
using type = std::conditional_t<IsArgTypeBuffer<GetArgType<X>()>::value, std::true_type, std::false_type>;
};
template<ArgType ArgT>
struct IsArgTypeInData {
static constexpr bool value = ArgT == ArgType::InData || ArgT == ArgType::PidDesc;
};
struct ArgTypeInDataFilter {
template<typename X>
using type = std::conditional_t<IsArgTypeInData<GetArgType<X>()>::value, std::true_type, std::false_type>;
};
template<typename T>
struct RawDataHelper {
static_assert(GetArgType<T>() == ArgType::InData || GetArgType<T>() == ArgType::PidDesc);
static constexpr size_t align = (GetArgType<T>() == ArgType::InData) ? __alignof__(T) : __alignof__(u64);
static constexpr size_t size = (GetArgType<T>() == ArgType::InData) ? sizeof(T) : sizeof(u64);
};
template<typename T>
struct RawDataHelper<Out<T>> {
static_assert(GetArgType<T>() == ArgType::InData);
static constexpr size_t align = __alignof(T);
static constexpr size_t size = sizeof(T);
};
template<typename Ts>
struct RawDataComputer;
template<typename... Ts>
struct RawDataComputer<std::tuple<Ts...>> {
/* https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,2604 */
static constexpr void QuickSort(std::array<size_t, sizeof...(Ts)> &map, std::array<size_t, sizeof...(Ts)> &values, int left, int right) {
do {
int i = left;
int j = right;
int x = map[i + ((j - i) >> 1)];
do {
while (i < static_cast<int>(sizeof...(Ts)) && values[x] > values[map[i]]) i++;
while (j >= 0 && values[x] < values[map[j]]) j--;
if (i > j) break;
if (i < j) {
const size_t temp = map[i];
map[i] = map[j];
map[j] = temp;
}
i++;
j--;
} while (i <= j);
if (j - left <= right - i) {
if (left < j) QuickSort(map, values, left, j);
left = i;
} else {
if (i < right) QuickSort(map, values, i, right);
right = j;
}
} while (left < right);
}
static constexpr void StableSort(std::array<size_t, sizeof...(Ts)> &map, std::array<size_t, sizeof...(Ts)> &values) {
/* First, quicksort a copy of the map. */
std::array<size_t, sizeof...(Ts)> map_unstable(map);
QuickSort(map_unstable, values, 0, sizeof...(Ts)-1);
/* Now, create stable sorted map from unstably quicksorted indices (via repeated insertion sort on element runs). */
for (size_t i = 0; i < sizeof...(Ts); i++) {
map[i] = map_unstable[i];
for (ssize_t j = i-1; j >= 0 && values[map[j]] == values[map[j+1]] && map[j] > map[j+1]; j--) {
const size_t temp = map[j];
map[j] = map[j+1];
map[j+1] = temp;
}
}
}
static constexpr std::array<size_t, sizeof...(Ts)+1> GetOffsets() {
std::array<size_t, sizeof...(Ts)+1> offsets = {};
offsets[0] = 0;
if constexpr (sizeof...(Ts) > 0) {
/* Get size, alignment for each type. */
std::array<size_t, sizeof...(Ts)> sizes = { RawDataHelper<Ts>::size... };
std::array<size_t, sizeof...(Ts)> aligns = { RawDataHelper<Ts>::align... };
/* We want to sort...by alignment. */
std::array<size_t, sizeof...(Ts)> map = {};
for (size_t i = 0; i < sizeof...(Ts); i++) { map[i] = i; }
StableSort(map, aligns);
/* Iterate over sorted types. */
size_t cur_offset = 0;
for (size_t i = 0; i < sizeof...(Ts); i++) {
const size_t align = aligns[map[i]];
if (cur_offset % align != 0) {
cur_offset += align - (cur_offset % align);
}
offsets[map[i]] = cur_offset;
cur_offset += sizes[map[i]];
}
offsets[sizeof...(Ts)] = cur_offset;
}
return offsets;
}
static constexpr std::array<size_t, sizeof...(Ts)+1> offsets = GetOffsets();
};
template <typename _Args, typename _ReturnType>
struct CommandMetaInfo;
template<typename... _Args, typename _ReturnType>
struct CommandMetaInfo<std::tuple<_Args...>, _ReturnType> {
using Args = std::tuple<_Args...>;
using ReturnType = _ReturnType;
static constexpr bool ReturnsResult = std::is_same_v<ReturnType, Result>;
static constexpr bool ReturnsVoid = std::is_same_v<ReturnType, void>;
using InDatas = FilteredTypes<ArgTypeInDataFilter::type, _Args...>;
using OutDatas = FilteredTypes<ArgTypeFilter<ArgType::OutData>::type, _Args...>;
using InHandles = FilteredTypes<ArgTypeFilter<ArgType::InHandle>::type, _Args...>;
using OutHandles = FilteredTypes<ArgTypeFilter<ArgType::OutHandle>::type, _Args...>;
using InSessions = FilteredTypes<ArgTypeFilter<ArgType::InSession>::type, _Args...>;
using OutSessions = FilteredTypes<ArgTypeFilter<ArgType::OutSession>::type, _Args...>;
using PidDescs = FilteredTypes<ArgTypeFilter<ArgType::PidDesc>::type, _Args...>;
using InBuffers = FilteredTypes<ArgTypeFilter<ArgType::InBuffer>::type, _Args...>;
using OutBuffers = FilteredTypes<ArgTypeFilter<ArgType::OutBuffer>::type, _Args...>;
using InPointers = FilteredTypes<ArgTypeFilter<ArgType::InPointer>::type, _Args...>;
using ClientSizeOutPointers = FilteredTypes<ArgTypeFilter<ArgType::OutPointerClientSize>::type, _Args...>;
using ServerSizeOutPointers = FilteredTypes<ArgTypeFilter<ArgType::OutPointerServerSize>::type, _Args...>;
using Buffers = FilteredTypes<ArgTypeBufferFilter::type, _Args...>;
static constexpr size_t NumInDatas = std::tuple_size_v<InDatas>;
static constexpr size_t NumOutDatas = std::tuple_size_v<OutDatas>;
static constexpr size_t NumInHandles = std::tuple_size_v<InHandles>;
static constexpr size_t NumOutHandles = std::tuple_size_v<OutHandles>;
static constexpr size_t NumInSessions = std::tuple_size_v<InSessions>;
static constexpr size_t NumOutSessions = std::tuple_size_v<OutSessions>;
static constexpr size_t NumPidDescs = std::tuple_size_v<PidDescs>;
static constexpr size_t NumInBuffers = std::tuple_size_v<InBuffers>;
static constexpr size_t NumOutBuffers = std::tuple_size_v<OutBuffers>;
static constexpr size_t NumInPointers = std::tuple_size_v<InPointers>;
static constexpr size_t NumClientSizeOutPointers = std::tuple_size_v<ClientSizeOutPointers>;
static constexpr size_t NumServerSizeOutPointers = std::tuple_size_v<ServerSizeOutPointers>;
static constexpr size_t NumBuffers = std::tuple_size_v<Buffers>;
static_assert(NumInSessions == 0, "InSessions not yet supported!");
static_assert(NumPidDescs == 0 || NumPidDescs == 1, "Methods can only take in 0 or 1 PIDs!");
static_assert(NumBuffers <= 8, "Methods can only take in <= 8 Buffers!");
static_assert(NumInHandles <= 8, "Methods can take in <= 8 Handles!");
static_assert(NumOutHandles + NumOutSessions <= 8, "Methods can only return <= 8 Handles+Sessions!");
static constexpr std::array<size_t, NumInDatas+1> InDataOffsets = RawDataComputer<InDatas>::offsets;
static constexpr size_t InRawArgSize = InDataOffsets[NumInDatas];
static constexpr size_t InRawArgSizeWithOutPointers = ((InRawArgSize + NumClientSizeOutPointers * sizeof(u16)) + 3) & ~3;
static constexpr std::array<size_t, NumOutDatas+1> OutDataOffsets = RawDataComputer<OutDatas>::offsets;
static constexpr size_t OutRawArgSize = OutDataOffsets[NumOutDatas];
};
/* ================================================================================= */
/* Actual wrapping implementation goes here. */
/* Validator. */
struct Validator {
template <typename T>
static constexpr bool ValidateCommandArgument(IpcResponseContext *ctx, size_t& a_index, size_t& b_index, size_t& x_index, size_t& h_index, size_t& cur_c_size_offset, size_t& total_c_size) {
constexpr ArgType argT = GetArgType<T>();
if constexpr (argT == ArgType::InBuffer) {
return (ctx->request.Buffers[a_index] != nullptr || ctx->request.BufferSizes[a_index] == 0) && ctx->request.BufferDirections[a_index] == BufferDirection_Send && ctx->request.BufferTypes[a_index++] == T::expected_type;
} else if constexpr (argT == ArgType::OutBuffer) {
return (ctx->request.Buffers[b_index] != nullptr || ctx->request.BufferSizes[b_index] == 0) && ctx->request.BufferDirections[b_index] == BufferDirection_Recv && ctx->request.BufferTypes[b_index++] == T::expected_type;
} else if constexpr (argT == ArgType::InPointer) {
return ctx->request.Statics[x_index++] != nullptr;
} else if constexpr (argT == ArgType::InHandle) {
if constexpr (std::is_same_v<T, MovedHandle>) {
return !ctx->request.WasHandleCopied[h_index++];
} else if constexpr (std::is_same_v<T, CopiedHandle>) {
return ctx->request.WasHandleCopied[h_index++];
}
} else {
if constexpr (argT == ArgType::OutPointerServerSize) {
total_c_size += T::num_elements * sizeof(T);
} else if constexpr (argT == ArgType::OutPointerClientSize) {
total_c_size += *((u16 *)((uintptr_t)(ctx->request.Raw) + 0x10 + cur_c_size_offset));
cur_c_size_offset += sizeof(u16);
}
return true;
}
}
template <typename Ts>
struct ValidateCommandTuple;
template <typename ...Ts>
struct ValidateCommandTuple<std::tuple<Ts...>> {
static constexpr bool IsValid(IpcResponseContext *ctx, size_t& a_index, size_t& b_index, size_t& x_index, size_t& h_index, size_t& cur_c_size_offset, size_t& total_c_size) {
return (ValidateCommandArgument<Ts>(ctx, a_index, b_index, x_index, h_index, cur_c_size_offset, total_c_size) && ...);
}
};
template<typename MetaInfo>
static constexpr Result Validate(IpcResponseContext *ctx) {
if (ctx->request.RawSize < MetaInfo::InRawArgSizeWithOutPointers) {
return ResultKernelConnectionClosed;
}
if (ctx->request.NumBuffers != MetaInfo::NumInBuffers + MetaInfo::NumOutBuffers) {
return ResultKernelConnectionClosed;
}
if (ctx->request.NumStatics != MetaInfo::NumInPointers) {
return ResultKernelConnectionClosed;
}
if (ctx->request.NumStaticsOut != MetaInfo::NumClientSizeOutPointers + MetaInfo::NumServerSizeOutPointers) {
return ResultKernelConnectionClosed;
}
if (ctx->request.NumHandles != MetaInfo::NumInHandles) {
return ResultKernelConnectionClosed;
}
if ((ctx->request.HasPid && MetaInfo::NumPidDescs == 0) || (!ctx->request.HasPid && MetaInfo::NumPidDescs != 0)) {
return ResultKernelConnectionClosed;
}
if (((u32 *)ctx->request.Raw)[0] != SFCI_MAGIC) {
return ResultKernelConnectionClosed;
}
size_t a_index = 0, b_index = MetaInfo::NumInBuffers, x_index = 0, h_index = 0;
size_t cur_c_size_offset = MetaInfo::InRawArgSize + (0x10 - ((uintptr_t)ctx->request.Raw - (ctx->request.IsDomainRequest ? sizeof(DomainMessageHeader) : 0) - (uintptr_t)ctx->request.RawWithoutPadding));
size_t total_c_size = 0;
if (!ValidateCommandTuple<typename MetaInfo::Args>::IsValid(ctx, a_index, b_index, x_index, h_index, cur_c_size_offset, total_c_size)) {
return ResultKernelConnectionClosed;
}
if (total_c_size > ctx->pb_size) {
return ResultKernelConnectionClosed;
}
return ResultSuccess;
}
};
/* ================================================================================= */
/* Decoder. */
template<typename MetaInfo>
struct Decoder {
template<typename T>
static constexpr T DecodeCommandArgument(IpcResponseContext *ctx, size_t& a_index, size_t& b_index, size_t& x_index, size_t& c_index, size_t& in_h_index, size_t& out_h_index, size_t& out_obj_index, size_t& in_data_index, size_t& out_data_index, size_t& pb_offset, size_t& c_sz_offset) {
constexpr ArgType argT = GetArgType<T>();
if constexpr (argT == ArgType::InBuffer) {
const T& value = T(ctx->request.Buffers[a_index], ctx->request.BufferSizes[a_index], ctx->request.BufferTypes[a_index]);
++a_index;
return value;
} else if constexpr (argT == ArgType::OutBuffer) {
const T& value = T(ctx->request.Buffers[b_index], ctx->request.BufferSizes[b_index], ctx->request.BufferTypes[b_index]);
++b_index;
return value;
} else if constexpr (argT == ArgType::InPointer) {
const T& value = T(ctx->request.Statics[x_index], ctx->request.StaticSizes[x_index]);
++x_index;
return value;
} else if constexpr (argT == ArgType::InHandle) {
return T(ctx->request.Handles[in_h_index++]);
} else if constexpr (argT == ArgType::OutHandle) {
return T(&ctx->out_handles[out_h_index++]);
} else if constexpr (argT == ArgType::PidDesc) {
uintptr_t ptr = ((uintptr_t)ctx->request.Raw + 0x10 + MetaInfo::InDataOffsets[in_data_index++]);
*(u64 *)ptr = ctx->request.Pid;
return T(ctx->request.Pid);
} else if constexpr (argT == ArgType::InData) {
uintptr_t ptr = ((uintptr_t)ctx->request.Raw + 0x10 + MetaInfo::InDataOffsets[in_data_index++]);
if constexpr (std::is_same_v<bool, T>) {
return *((u8 *)ptr) & 1;
} else {
return *((T *)ptr);
}
} else if constexpr (argT == ArgType::OutData) {
uintptr_t ptr = ((uintptr_t)ctx->out_data + MetaInfo::OutDataOffsets[out_data_index++]);
return T(reinterpret_cast<typename OutHelper<T>::type *>(ptr));
} else if constexpr (argT == ArgType::OutPointerClientSize || argT == ArgType::OutPointerServerSize) {
u16 sz;
if constexpr(argT == ArgType::OutPointerServerSize) {
sz = T::element_size;
} else {
sz = *(const u16 *)((uintptr_t)ctx->request.Raw + 0x10 + c_sz_offset);
}
u8* buf = ctx->pb + pb_offset;
c_sz_offset += sizeof(u16);
pb_offset += sz;
ipcAddSendStatic(&ctx->reply, buf, sz, c_index++);
return T(buf, sz);
} else if constexpr (argT == ArgType::OutSession) {
if (IsDomainObject(ctx->obj_holder)) {
const T& value = T(ctx->out_objs[out_obj_index], ctx->obj_holder->GetServiceObject<IDomainObject>(), &ctx->out_object_ids[out_obj_index]);
out_obj_index++;
return value;
} else {
const T& value = T(ctx->out_objs[out_obj_index], nullptr, 0);
out_obj_index++;
return value;
}
}
}
template <typename Ts>
struct DecodeTuple;
template <typename ...Ts>
struct DecodeTuple<std::tuple<Ts...>> {
static constexpr std::tuple<Ts...> GetArgs(IpcResponseContext *ctx, size_t& a_index, size_t& b_index, size_t& x_index, size_t& c_index, size_t& in_h_index, size_t& out_h_index, size_t& out_obj_index, size_t& in_data_index, size_t& out_data_index, size_t& pb_offset, size_t& c_sz_offset) {
return std::tuple<Ts... > {
DecodeCommandArgument<Ts>(ctx, a_index, b_index, x_index, c_index, in_h_index, out_h_index, out_obj_index, in_data_index, out_data_index, pb_offset, c_sz_offset)
...
};
}
};
static constexpr typename MetaInfo::Args Decode(IpcResponseContext *ctx) {
size_t a_index = 0, b_index = MetaInfo::NumInBuffers, x_index = 0, c_index = 0, in_h_index = 0, out_h_index = 0, out_obj_index = 0;
size_t in_data_index = 0x0, out_data_index = 0, pb_offset = 0;
size_t c_sz_offset = MetaInfo::InRawArgSize + (0x10 - ((uintptr_t)ctx->request.Raw - (ctx->request.IsDomainRequest ? sizeof(DomainMessageHeader) : 0) - (uintptr_t)ctx->request.RawWithoutPadding));
return DecodeTuple<typename MetaInfo::Args>::GetArgs(ctx, a_index, b_index, x_index, c_index, in_h_index, out_h_index, out_obj_index, in_data_index, out_data_index, pb_offset, c_sz_offset);
}
};
/* ================================================================================= */
template<typename MetaInfo, typename T>
static constexpr void EncodeArgument(IpcResponseContext *ctx, size_t&out_obj_index, T& arg) {
constexpr ArgType argT = GetArgType<T>();
if constexpr (argT == ArgType::OutHandle) {
if constexpr (std::is_same_v<MovedHandle, typename OutHelper<T>::type>) {
ipcSendHandleMove(&ctx->reply, arg.GetValue().handle);
} else {
ipcSendHandleCopy(&ctx->reply, arg.GetValue().handle);
}
} else if constexpr (argT == ArgType::OutSession) {
if (IsDomainObject(ctx->obj_holder)) {
auto domain = ctx->obj_holder->GetServiceObject<IDomainObject>();
domain->SetObject(arg.GetObjectId(), std::move(arg.GetHolder()));
} else {
ctx->manager->AddSession(ctx->out_object_server_handles[out_obj_index++], std::move(arg.GetHolder()));
}
}
}
template<typename MetaInfo, typename ArgsTuple>
struct Encoder;
template <typename MetaInfo, typename ...Args>
struct Encoder<MetaInfo, std::tuple<Args...>> {
static constexpr void EncodeFailure(IpcResponseContext *ctx, Result rc) {
memset(armGetTls(), 0, 0x100);
ipcInitialize(&ctx->reply);
struct {
u64 magic;
u64 result;
} *raw;
if (IsDomainObject(ctx->obj_holder)) {
raw = (decltype(raw))ipcPrepareHeaderForDomain(&ctx->reply, sizeof(*raw), 0);
auto resp_header = (DomainResponseHeader *)((uintptr_t)raw - sizeof(DomainResponseHeader));
*resp_header = {};
} else {
raw = (decltype(raw))ipcPrepareHeader(&ctx->reply, sizeof(*raw));
}
raw->magic = SFCO_MAGIC;
raw->result = rc;
}
static constexpr void EncodeSuccess(IpcResponseContext *ctx, Args... args) {
size_t out_obj_index = 0;
((EncodeArgument<MetaInfo, Args>(ctx, out_obj_index, args)), ...);
const bool is_domain = IsDomainObject(ctx->obj_holder);
if (!is_domain) {
for (unsigned int i = 0; i < MetaInfo::NumOutSessions; i++) {
ipcSendHandleMove(&ctx->reply, ctx->out_handles[MetaInfo::NumOutHandles + i].handle);
}
}
struct {
u64 magic;
u64 result;
} *raw;
if (is_domain) {
raw = (decltype(raw))ipcPrepareHeaderForDomain(&ctx->reply, sizeof(*raw) + MetaInfo::OutRawArgSize + sizeof(*ctx->out_object_ids) * MetaInfo::NumOutSessions, 0);
auto resp_header = (DomainResponseHeader *)((uintptr_t)raw - sizeof(DomainResponseHeader));
*resp_header = {};
resp_header->NumObjectIds = MetaInfo::NumOutSessions;
} else {
raw = (decltype(raw))ipcPrepareHeader(&ctx->reply, sizeof(*raw)+ MetaInfo::OutRawArgSize);
}
raw->magic = SFCO_MAGIC;
raw->result = 0;
memcpy((void *)((uintptr_t)raw + sizeof(*raw)), ctx->out_data, MetaInfo::OutRawArgSize);
if (is_domain) {
memcpy((void *)((uintptr_t)raw + sizeof(*raw) + MetaInfo::OutRawArgSize), ctx->out_object_ids, sizeof(*ctx->out_object_ids) * MetaInfo::NumOutSessions);
}
}
};
/* ================================================================================= */
template<auto MemberFunction>
struct MemberFunctionTraits {
private:
template<typename R, typename C, typename... A>
static R GetReturnTypeImpl(R(C::*)(A...));
template<typename R, typename C, typename... A>
static C GetClassTypeImpl(R(C::*)(A...));
template<typename R, typename C, typename... A>
static std::tuple<A...> GetArgsImpl(R(C::*)(A...));
public:
using ReturnType = decltype(GetReturnTypeImpl(MemberFunction));
using ClassType = decltype(GetClassTypeImpl(MemberFunction));
using ArgsType = decltype(GetArgsImpl(MemberFunction));
};
template<auto IpcCommandImpl, typename ClassType = typename MemberFunctionTraits<IpcCommandImpl>::ClassType>
constexpr Result WrapIpcCommandImpl(IpcResponseContext *ctx) {
using Traits = MemberFunctionTraits<IpcCommandImpl>;
using ArgsType = typename Traits::ArgsType;
using ReturnType = typename Traits::ReturnType;
using BaseClassType = typename Traits::ClassType;
static_assert(std::is_base_of_v<BaseClassType, ClassType>, "Override class type incorrect");
using CommandMetaData = CommandMetaInfo<ArgsType, ReturnType>;
static_assert(CommandMetaData::ReturnsResult || CommandMetaData::ReturnsVoid, "IpcCommandImpls must return Result or void");
ipcInitialize(&ctx->reply);
memset(ctx->out_data, 0, CommandMetaData::OutRawArgSize);
R_TRY(Validator::Validate<CommandMetaData>(ctx));
ClassType *this_ptr = nullptr;
if (IsDomainObject(ctx->obj_holder)) {
this_ptr = ctx->obj_holder->GetServiceObject<IDomainObject>()->GetObject(ctx->request.InThisObjectId)->GetServiceObject<ClassType>();
} else {
this_ptr = ctx->obj_holder->GetServiceObject<ClassType>();
}
if (this_ptr == nullptr) {
return ResultServiceFrameworkTargetNotFound;
}
size_t num_out_objects;
std::shared_ptr<IServiceObject> out_objects[CommandMetaData::NumOutSessions];
auto cleanup_guard = SCOPE_GUARD {
/* Clean up objects as necessary. */
if (IsDomainObject(ctx->obj_holder)) {
for (unsigned int i = 0; i < num_out_objects; i++) {
ctx->obj_holder->GetServiceObject<IDomainObject>()->FreeObject(ctx->out_object_ids[i]);
}
} else {
for (unsigned int i = 0; i < num_out_objects; i++) {
svcCloseHandle(ctx->out_object_server_handles[i]);
svcCloseHandle(ctx->out_handles[CommandMetaData::NumOutHandles + i].handle);
}
}
for (unsigned int i = 0; i < num_out_objects; i++) {
ctx->out_objs[i] = nullptr;
}
};
/* Allocate out object IDs. */
if (IsDomainObject(ctx->obj_holder)) {
for (num_out_objects = 0; num_out_objects < CommandMetaData::NumOutSessions; num_out_objects++) {
R_TRY_CLEANUP(ctx->obj_holder->GetServiceObject<IDomainObject>()->ReserveObject(&ctx->out_object_ids[num_out_objects]), {
std::apply(Encoder<CommandMetaData, typename CommandMetaData::Args>::EncodeFailure, std::tuple_cat(std::make_tuple(ctx), std::make_tuple(R_CLEANUP_RESULT)));
});
ctx->out_objs[num_out_objects] = &out_objects[num_out_objects];
}
} else {
for (num_out_objects = 0; num_out_objects < CommandMetaData::NumOutSessions; num_out_objects++) {
Handle server_h, client_h;
R_TRY_CLEANUP(SessionManagerBase::CreateSessionHandles(&server_h, &client_h), {
std::apply(Encoder<CommandMetaData, typename CommandMetaData::Args>::EncodeFailure, std::tuple_cat(std::make_tuple(ctx), std::make_tuple(R_CLEANUP_RESULT)));
});
ctx->out_object_server_handles[num_out_objects] = server_h;
ctx->out_handles[CommandMetaData::NumOutHandles + num_out_objects].handle = client_h;
ctx->out_objs[num_out_objects] = &out_objects[num_out_objects];
}
}
/* Decode, apply, encode. */
{
auto args = Decoder<CommandMetaData>::Decode(ctx);
if constexpr (CommandMetaData::ReturnsResult) {
R_TRY_CLEANUP(std::apply( [=](auto&&... args) { return (this_ptr->*IpcCommandImpl)(args...); }, args), {
std::apply(Encoder<CommandMetaData, decltype(args)>::EncodeFailure, std::tuple_cat(std::make_tuple(ctx), std::make_tuple(R_CLEANUP_RESULT)));
});
} else {
std::apply( [=](auto&&... args) { (this_ptr->*IpcCommandImpl)(args...); }, args);
}
std::apply(Encoder<CommandMetaData, decltype(args)>::EncodeSuccess, std::tuple_cat(std::make_tuple(ctx), args));
}
/* Cancel object guard, clear remaining object references. */
cleanup_guard.Cancel();
for (unsigned int i = 0; i < num_out_objects; i++) {
ctx->out_objs[i] = nullptr;
}
return ResultSuccess;
}
template <auto CommandId, auto CommandImpl, typename OverrideClassType, FirmwareVersion Low = FirmwareVersion_Min, FirmwareVersion High = FirmwareVersion_Max>
inline static constexpr ServiceCommandMeta MakeServiceCommandMeta() {
return {
.fw_low = Low,
.fw_high = High,
.cmd_id = static_cast<u32>(CommandId),
.handler = WrapIpcCommandImpl<CommandImpl, OverrideClassType>,
};
};
#define MAKE_SERVICE_COMMAND_META(class, name, ...) MakeServiceCommandMeta<CommandId::name, &class::name, class, ##__VA_ARGS__>()
#pragma GCC diagnostic pop

View File

@@ -0,0 +1,135 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include <memory>
#include <type_traits>
#include "ipc_out.hpp"
#include "../firmware_version.hpp"
class IpcResponseContext;
struct ServiceCommandMeta {
FirmwareVersion fw_low = FirmwareVersion_Max;
FirmwareVersion fw_high = FirmwareVersion_Max;
u32 cmd_id = 0;
Result (*handler)(IpcResponseContext *) = nullptr;
};
class IServiceObject {
public:
virtual ~IServiceObject() { }
virtual bool IsMitmObject() const { return false; }
};
#define SERVICE_DISPATCH_TABLE_NAME s_DispatchTable
#define DEFINE_SERVICE_DISPATCH_TABLE static constexpr ServiceCommandMeta SERVICE_DISPATCH_TABLE_NAME[]
template <typename T>
static constexpr size_t DispatchTableEntryCount() {
static_assert(std::is_base_of<IServiceObject, T>::value, "DispatchTable owners must derive from IServiceObject");
return sizeof(T::SERVICE_DISPATCH_TABLE_NAME)/sizeof(ServiceCommandMeta);
}
template <typename T>
static constexpr const ServiceCommandMeta* DispatchTable() {
static_assert(std::is_base_of<IServiceObject, T>::value, "DispatchTable owners must derive from IServiceObject");
return reinterpret_cast<const ServiceCommandMeta*>(&T::SERVICE_DISPATCH_TABLE_NAME);
}
template <typename T>
static constexpr uintptr_t ServiceObjectId() {
static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject");
return reinterpret_cast<uintptr_t>(&T::SERVICE_DISPATCH_TABLE_NAME);
}
class ServiceObjectHolder {
private:
std::shared_ptr<IServiceObject> srv;
const ServiceCommandMeta *dispatch_table;
size_t entry_count;
/* Private copy constructor. */
ServiceObjectHolder(const ServiceObjectHolder& other) : srv(other.srv), dispatch_table(other.dispatch_table), entry_count(other.entry_count) { }
ServiceObjectHolder& operator=(const ServiceObjectHolder& other);
public:
/* Templated constructor ensures correct type id at runtime. */
template <typename ServiceImpl>
explicit ServiceObjectHolder(std::shared_ptr<ServiceImpl>&& s) : srv(std::move(s)), dispatch_table(DispatchTable<ServiceImpl>()), entry_count(DispatchTableEntryCount<ServiceImpl>()) { }
template <typename ServiceImpl>
ServiceImpl *GetServiceObject() const {
if (GetServiceId() == ServiceObjectId<ServiceImpl>()) {
return static_cast<ServiceImpl *>(this->srv.get());
}
return nullptr;
}
template<typename ServiceImpl>
ServiceImpl *GetServiceObjectUnsafe() const {
return static_cast<ServiceImpl *>(this->srv.get());
}
const ServiceCommandMeta *GetDispatchTable() const {
return this->dispatch_table;
}
size_t GetDispatchTableEntryCount() const {
return this->entry_count;
}
constexpr uintptr_t GetServiceId() const {
return reinterpret_cast<uintptr_t>(this->dispatch_table);
}
bool IsMitmObject() const {
return this->srv->IsMitmObject();
}
/* Default constructor, move constructor, move assignment operator. */
ServiceObjectHolder() : srv(nullptr), dispatch_table(nullptr) { }
ServiceObjectHolder(ServiceObjectHolder&& other) : srv(std::move(other.srv)), dispatch_table(std::move(other.dispatch_table)), entry_count(std::move(other.entry_count)) { }
ServiceObjectHolder& operator=(ServiceObjectHolder&& other) {
this->srv = other.srv;
this->dispatch_table = other.dispatch_table;
this->entry_count = other.entry_count;
other.Reset();
return *this;
}
explicit operator bool() const {
return this->srv != nullptr;
}
bool operator!() const {
return this->srv == nullptr;
}
void Reset() {
this->srv.reset();
this->dispatch_table = nullptr;
this->entry_count = 0;
}
ServiceObjectHolder Clone() const {
ServiceObjectHolder clone(*this);
return clone;
}
};

View File

@@ -0,0 +1,344 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "../results.hpp"
#include "../iwaitable.hpp"
#include "ipc_service_object.hpp"
#include "ipc_serialization.hpp"
class ServiceSession : public IWaitable
{
protected:
Handle session_handle;
std::vector<unsigned char> pointer_buffer;
ServiceObjectHolder obj_holder;
ServiceObjectHolder control_holder = ServiceObjectHolder(std::make_shared<IHipcControlService>(this));
u8 backup_tls[0x100];
ServiceSession(Handle s_h) : session_handle(s_h) { }
public:
template<typename T>
ServiceSession(Handle s_h, size_t pbs) : session_handle(s_h), pointer_buffer(pbs), obj_holder(std::make_shared<T>()) { }
ServiceSession(Handle s_h, size_t pbs, ServiceObjectHolder &&h) : session_handle(s_h), pointer_buffer(pbs), obj_holder(std::move(h)) { }
virtual ~ServiceSession() override {
svcCloseHandle(this->session_handle);
}
SessionManagerBase *GetSessionManager() {
return static_cast<SessionManagerBase *>(this->GetManager());
}
DomainManager *GetDomainManager() {
return static_cast<DomainManager *>(this->GetSessionManager());
}
Result Receive() {
int handle_index;
/* Prepare pointer buffer... */
IpcCommand c;
ipcInitialize(&c);
if (this->pointer_buffer.size() > 0) {
ipcAddRecvStatic(&c, this->pointer_buffer.data(), this->pointer_buffer.size(), 0);
ipcPrepareHeader(&c, 0);
/* Fix libnx bug in serverside C descriptor handling. */
((u32 *)armGetTls())[1] &= 0xFFFFC3FF;
((u32 *)armGetTls())[1] |= (2) << 10;
} else {
ipcPrepareHeader(&c, 0);
}
/* Receive. */
R_TRY(svcReplyAndReceive(&handle_index, &this->session_handle, 1, 0, U64_MAX));
std::memcpy(this->backup_tls, armGetTls(), sizeof(this->backup_tls));
return ResultSuccess;
}
Result Reply() {
int handle_index;
return svcReplyAndReceive(&handle_index, &this->session_handle, 0, this->session_handle, 0);
}
/* For preparing basic replies. */
Result PrepareBasicResponse(IpcResponseContext *ctx, Result rc) {
ipcInitialize(&ctx->reply);
struct {
u64 magic;
u64 result;
} *raw = (decltype(raw))ipcPrepareHeader(&ctx->reply, sizeof(*raw));
raw->magic = SFCO_MAGIC;
raw->result = rc;
return raw->result;
}
Result PrepareBasicDomainResponse(IpcResponseContext *ctx, Result rc) {
ipcInitialize(&ctx->reply);
struct {
DomainResponseHeader hdr;
u64 magic;
u64 result;
} *raw = (decltype(raw))ipcPrepareHeader(&ctx->reply, sizeof(*raw));
raw->hdr = {};
raw->magic = SFCO_MAGIC;
raw->result = rc;
return raw->result;
}
/* For making a new response context. */
void InitializeResponseContext(IpcResponseContext *ctx) {
std::memset(ctx, 0, sizeof(*ctx));
ctx->manager = this->GetSessionManager();
ctx->obj_holder = &this->obj_holder;
ctx->pb = this->pointer_buffer.data();
ctx->pb_size = this->pointer_buffer.size();
}
/* IWaitable */
virtual Handle GetHandle() {
return this->session_handle;
}
virtual Result GetResponse(IpcResponseContext *ctx) {
FirmwareVersion fw = GetRuntimeFirmwareVersion();
const ServiceCommandMeta *dispatch_table = ctx->obj_holder->GetDispatchTable();
size_t entry_count = ctx->obj_holder->GetDispatchTableEntryCount();
if (IsDomainObject(ctx->obj_holder)) {
switch (ctx->request.InMessageType) {
case DomainMessageType_Invalid:
return ResultKernelConnectionClosed;
case DomainMessageType_Close:
return PrepareBasicDomainResponse(ctx, ctx->obj_holder->GetServiceObject<IDomainObject>()->FreeObject(ctx->request.InThisObjectId));
case DomainMessageType_SendMessage:
{
auto sub_obj = ctx->obj_holder->GetServiceObject<IDomainObject>()->GetObject(ctx->request.InThisObjectId);
if (sub_obj == nullptr) {
return PrepareBasicDomainResponse(ctx, ResultHipcDomainObjectNotFound);
}
dispatch_table = sub_obj->GetDispatchTable();
entry_count = sub_obj->GetDispatchTableEntryCount();
}
}
}
for (size_t i = 0; i < entry_count; i++) {
if (ctx->cmd_id == dispatch_table[i].cmd_id && dispatch_table[i].fw_low <= fw && fw <= dispatch_table[i].fw_high) {
R_TRY(dispatch_table[i].handler(ctx));
break;
}
}
return ResultSuccess;
}
virtual Result HandleReceived() {
IpcResponseContext ctx;
this->InitializeResponseContext(&ctx);
ctx.cmd_type = (IpcCommandType)(*(u16 *)(armGetTls()));
ctx.rc = ResultSuccess;
/* Parse based on command type. */
switch (ctx.cmd_type) {
case IpcCommandType_Invalid:
case IpcCommandType_LegacyRequest:
case IpcCommandType_LegacyControl:
return ResultKernelConnectionClosed;
case IpcCommandType_Close:
{
/* Clean up gracefully. */
PrepareBasicResponse(&ctx, 0);
this->Reply();
}
return ResultKernelConnectionClosed;
case IpcCommandType_Control:
case IpcCommandType_ControlWithContext:
ctx.rc = ipcParse(&ctx.request);
ctx.obj_holder = &this->control_holder;
break;
case IpcCommandType_Request:
case IpcCommandType_RequestWithContext:
if (IsDomainObject(&this->obj_holder)) {
ctx.rc = ipcParseDomainRequest(&ctx.request);
} else {
ctx.rc = ipcParse(&ctx.request);
}
break;
default:
return ResultKernelConnectionClosed;
}
if (R_SUCCEEDED(ctx.rc)) {
ctx.cmd_id = ((u32 *)ctx.request.Raw)[2];
this->PreProcessRequest(&ctx);
ctx.rc = this->GetResponse(&ctx);
}
if (ctx.rc == ResultServiceFrameworkRequestDeferredByUser) {
/* Session defer. */
this->SetDeferred(true);
} else if (ctx.rc == ResultKernelConnectionClosed) {
/* Session close, nothing to do. */
} else {
if (R_SUCCEEDED(ctx.rc)) {
this->PostProcessResponse(&ctx);
}
ctx.rc = this->Reply();
if (ctx.rc == ResultKernelTimedOut) {
ctx.rc = ResultSuccess;
}
this->CleanupResponse(&ctx);
}
return ctx.rc;
}
virtual Result HandleDeferred() override {
memcpy(armGetTls(), this->backup_tls, sizeof(this->backup_tls));
auto defer_guard = SCOPE_GUARD {
this->SetDeferred(false);
};
R_TRY_CATCH(this->HandleReceived()) {
R_CATCH(ResultServiceFrameworkRequestDeferredByUser) {
defer_guard.Cancel();
return ResultServiceFrameworkRequestDeferredByUser;
}
} R_END_TRY_CATCH;
return ResultSuccess;
}
virtual Result HandleSignaled(u64 timeout) {
R_TRY(this->Receive());
R_TRY(this->HandleReceived());
return ResultSuccess;
}
virtual void PreProcessRequest(IpcResponseContext *ctx) {
/* ... */
(void)(ctx);
}
virtual void PostProcessResponse(IpcResponseContext *ctx) {
/* ... */
(void)(ctx);
}
virtual void CleanupResponse(IpcResponseContext *ctx) {
std::memset(this->backup_tls, 0, sizeof(this->backup_tls));
}
public:
class IHipcControlService : public IServiceObject {
private:
enum class CommandId {
ConvertCurrentObjectToDomain = 0,
CopyFromCurrentDomain = 1,
CloneCurrentObject = 2,
QueryPointerBufferSize = 3,
CloneCurrentObjectEx = 4,
};
private:
ServiceSession *session;
public:
explicit IHipcControlService(ServiceSession *s) : session(s) {
}
virtual ~IHipcControlService() override { }
Result ConvertCurrentObjectToDomain(Out<u32> object_id) {
/* Allocate new domain. */
auto new_domain = this->session->GetDomainManager()->AllocateDomain();
if (new_domain == nullptr) {
return ResultHipcOutOfDomains;
}
/* Reserve an object in the domain for our session. */
u32 reserved_id;
R_TRY(new_domain->ReserveObject(&reserved_id));
new_domain->SetObject(reserved_id, std::move(this->session->obj_holder));
this->session->obj_holder = std::move(ServiceObjectHolder(std::move(new_domain)));
/* Return the object id. */
object_id.SetValue(reserved_id);
return ResultSuccess;
}
Result CopyFromCurrentDomain(Out<MovedHandle> out_h, u32 id) {
auto domain = this->session->obj_holder.GetServiceObject<IDomainObject>();
if (domain == nullptr) {
return ResultHipcTargetNotDomain;
}
auto object = domain->GetObject(id);
if (object == nullptr) {
return ResultHipcDomainObjectNotFound;
}
Handle server_h, client_h;
R_ASSERT(SessionManagerBase::CreateSessionHandles(&server_h, &client_h));
this->session->GetSessionManager()->AddSession(server_h, std::move(object->Clone()));
out_h.SetValue(client_h);
return ResultSuccess;
}
void CloneCurrentObject(Out<MovedHandle> out_h) {
Handle server_h, client_h;
R_ASSERT(SessionManagerBase::CreateSessionHandles(&server_h, &client_h));
this->session->GetSessionManager()->AddSession(server_h, std::move(this->session->obj_holder.Clone()));
out_h.SetValue(client_h);
}
void QueryPointerBufferSize(Out<u16> size) {
size.SetValue(this->session->pointer_buffer.size());
}
void CloneCurrentObjectEx(Out<MovedHandle> out_h, u32 which) {
/* TODO: Figure out what this u32 controls. */
return CloneCurrentObject(out_h);
}
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(ServiceSession::IHipcControlService, ConvertCurrentObjectToDomain),
MAKE_SERVICE_COMMAND_META(ServiceSession::IHipcControlService, CopyFromCurrentDomain),
MAKE_SERVICE_COMMAND_META(ServiceSession::IHipcControlService, CloneCurrentObject),
MAKE_SERVICE_COMMAND_META(ServiceSession::IHipcControlService, QueryPointerBufferSize),
MAKE_SERVICE_COMMAND_META(ServiceSession::IHipcControlService, CloneCurrentObjectEx),
};
};
};

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2018-2019 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 <atomic>
#include "../waitable_manager_base.hpp"
#include "ipc_service_object.hpp"
class SessionManagerBase : public WaitableManagerBase, public DomainManager {
public:
SessionManagerBase() = default;
virtual ~SessionManagerBase() = default;
virtual void AddSession(Handle server_h, ServiceObjectHolder &&service) = 0;
static Result CreateSessionHandles(Handle *server_h, Handle *client_h) {
return svcCreateSession(server_h, client_h, 0, 0);
}
};

View File

@@ -0,0 +1,152 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include <type_traits>
#include "ipc_out.hpp"
/* Represents an input PID. */
struct PidDescriptorTag{};
struct PidDescriptor : public PidDescriptorTag {
u64 pid;
void operator=(u64 &p) {
pid = p;
}
PidDescriptor(u64 p) : pid(p) { }
};
struct IpcHandleTag{};
struct IpcHandle : public IpcHandleTag {
Handle handle;
};
/* Represents a moved handle. */
struct MovedHandle : public IpcHandle {
void operator=(const Handle &h) {
this->handle = h;
}
void operator=(const IpcHandle &o) {
this->handle = o.handle;
}
MovedHandle(Handle h) {
this->handle = h;
}
Handle GetValue() const {
return this->handle;
}
};
/* Represents a copied handle. */
struct CopiedHandle : public IpcHandle {
void operator=(const Handle &h) {
handle = h;
}
void operator=(const IpcHandle &o) {
this->handle = o.handle;
}
CopiedHandle(Handle h) {
this->handle = h;
}
Handle GetValue() const {
return this->handle;
}
};
template <>
class Out<MovedHandle> : public OutHandleTag {
private:
MovedHandle *obj;
public:
Out(IpcHandle *o) : obj(static_cast<MovedHandle *>(o)) { }
void SetValue(const Handle& h) {
*obj = h;
}
void SetValue(const MovedHandle& o) {
*obj = o;
}
const MovedHandle& GetValue() {
return *obj;
}
MovedHandle* GetPointer() {
return obj;
}
Handle* GetHandlePointer() {
return &obj->handle;
}
/* Convenience operators. */
MovedHandle& operator*() {
return *obj;
}
MovedHandle* operator->() {
return obj;
}
};
template <>
class Out<CopiedHandle> : public OutHandleTag {
private:
CopiedHandle *obj;
public:
Out(IpcHandle *o) : obj(static_cast<CopiedHandle *>(o)) { }
void SetValue(const Handle& h) {
*obj = h;
}
void SetValue(const CopiedHandle& o) {
*obj = o;
}
const CopiedHandle& GetValue() {
return *obj;
}
CopiedHandle* GetPointer() {
return obj;
}
Handle* GetHandlePointer() {
return &obj->handle;
}
/* Convenience operators. */
CopiedHandle& operator*() {
return *obj;
}
CopiedHandle* operator->() {
return obj;
}
};

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "waitable_manager_base.hpp"
#include "hossynch.hpp"
class IWaitable {
private:
u64 wait_priority = 0;
bool is_deferred = false;
WaitableManagerBase *manager = nullptr;
protected:
HosMutex sig_lock;
bool is_signaled = false;
public:
virtual ~IWaitable() = default;
virtual Result HandleDeferred() {
/* By default, HandleDeferred panics, because object shouldn't be deferrable. */
std::abort();
}
bool IsSignaled() {
std::scoped_lock<HosMutex> lock(this->sig_lock);
return this->is_signaled;
}
virtual Handle GetHandle() = 0;
virtual Result HandleSignaled(u64 timeout) = 0;
WaitableManagerBase *GetManager() {
return this->manager;
}
void SetManager(WaitableManagerBase *m) {
this->manager = m;
}
void UpdatePriority() {
if (manager) {
this->wait_priority = this->manager->GetNextPriority();
}
}
bool IsDeferred() {
return this->is_deferred;
}
void SetDeferred(bool d) {
this->is_deferred = d;
}
static bool Compare(IWaitable *a, IWaitable *b) {
return (a->wait_priority < b->wait_priority) && !a->IsDeferred() && (a->GetHandle() != INVALID_HANDLE);
}
void NotifyManagerSignaled() {
if (this->manager) {
this->manager->NotifySignaled(this);
}
}
};

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "../defines.hpp"
#include "../results.hpp"
#include "kvdb_auto_buffer.hpp"
namespace sts::kvdb {
/* Functionality for parsing/generating a key value archive. */
class ArchiveReader {
private:
AutoBuffer &buffer;
size_t offset;
public:
ArchiveReader(AutoBuffer &b) : buffer(b), offset(0) { /* ... */ }
private:
Result Peek(void *dst, size_t size);
Result Read(void *dst, size_t size);
public:
Result ReadEntryCount(size_t *out);
Result GetEntrySize(size_t *out_key_size, size_t *out_value_size);
Result ReadEntry(void *out_key, size_t key_size, void *out_value, size_t value_size);
};
class ArchiveWriter {
private:
AutoBuffer &buffer;
size_t offset;
public:
ArchiveWriter(AutoBuffer &b) : buffer(b), offset(0) { /* ... */ }
private:
Result Write(const void *src, size_t size);
public:
void WriteHeader(size_t entry_count);
void WriteEntry(const void *key, size_t key_size, const void *value, size_t value_size);
};
class ArchiveSizeHelper {
private:
size_t size;
public:
ArchiveSizeHelper();
void AddEntry(size_t key_size, size_t value_size);
size_t GetSize() const {
return this->size;
}
};
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "../defines.hpp"
#include "../results.hpp"
namespace sts::kvdb {
class AutoBuffer {
NON_COPYABLE(AutoBuffer);
private:
u8 *buffer;
size_t size;
public:
AutoBuffer() : buffer(nullptr), size(0) { /* ... */ }
~AutoBuffer() {
this->Reset();
}
AutoBuffer(AutoBuffer &&rhs) {
this->buffer = rhs.buffer;
this->size = rhs.size;
rhs.buffer = nullptr;
rhs.size = 0;
}
AutoBuffer& operator=(AutoBuffer &&rhs) {
rhs.Swap(*this);
return *this;
}
void Swap(AutoBuffer &rhs) {
std::swap(this->buffer, rhs.buffer);
std::swap(this->size, rhs.size);
}
void Reset() {
if (this->buffer != nullptr) {
std::free(this->buffer);
this->buffer = nullptr;
this->size = 0;
}
}
u8 *Get() const {
return this->buffer;
}
size_t GetSize() const {
return this->size;
}
Result Initialize(size_t size) {
/* Check that we're not already initialized. */
if (this->buffer != nullptr) {
std::abort();
}
/* Allocate a buffer. */
this->buffer = static_cast<u8 *>(std::malloc(size));
if (this->buffer == nullptr) {
return ResultKvdbAllocationFailed;
}
this->size = size;
return ResultSuccess;
}
Result Initialize(const void *buf, size_t size) {
/* Create a new buffer of the right size. */
R_TRY(this->Initialize(size));
/* Copy the input data in. */
std::memcpy(this->buffer, buf, size);
return ResultSuccess;
}
};
}

View File

@@ -0,0 +1,152 @@
/*
* Copyright (c) 2018-2019 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 <cstring>
#include <cstdarg>
#include <switch.h>
#include "../defines.hpp"
#include "../results.hpp"
namespace sts::kvdb {
/* Represents a string with a backing buffer of N bytes. */
template<size_t N>
class BoundedString {
static_assert(N > 0, "BoundedString requires non-zero backing buffer!");
private:
char buffer[N];
private:
/* Utility. */
static inline void CheckLength(size_t len) {
if (len >= N) {
std::abort();
}
}
public:
/* Constructors. */
constexpr BoundedString() {
buffer[0] = 0;
}
explicit constexpr BoundedString(const char *s) {
this->Set(s);
}
/* Static constructors. */
static constexpr BoundedString<N> Make(const char *s) {
return BoundedString<N>(s);
}
static constexpr BoundedString<N> MakeFormat(const char *format, ...) __attribute__((format (printf, 1, 2))) {
BoundedString<N> string;
std::va_list args;
va_start(args, format);
CheckLength(std::vsnprintf(string.buffer, N, format, args));
va_end(args);
return string;
}
/* Getters. */
size_t GetLength() const {
return strnlen(this->buffer, N);
}
const char *Get() const {
return this->buffer;
}
operator const char *() const {
return this->buffer;
}
/* Setters. */
void Set(const char *s) {
/* Ensure string can fit in our buffer. */
CheckLength(strnlen(s, N));
std::strncpy(this->buffer, s, N);
}
void SetFormat(const char *format, ...) __attribute__((format (printf, 2, 3))) {
/* Format into the buffer, abort if too large. */
std::va_list args;
va_start(args, format);
CheckLength(std::vsnprintf(this->buffer, N, format, args));
va_end(args);
}
/* Append to existing. */
void Append(const char *s) {
const size_t length = GetLength();
CheckLength(length + strnlen(s, N));
std::strncat(this->buffer, s, N - length - 1);
}
void Append(char c) {
const size_t length = GetLength();
CheckLength(length + 1);
this->buffer[length] = c;
this->buffer[length + 1] = 0;
}
void AppendFormat(const char *format, ...) __attribute__((format (printf, 2, 3))) {
const size_t length = GetLength();
std::va_list args;
va_start(args, format);
CheckLength(std::vsnprintf(this->buffer + length, N - length, format, args) + length);
va_end(args);
}
/* Substring utilities. */
void GetSubstring(char *dst, size_t dst_size, size_t offset, size_t length) const {
/* Make sure output buffer can hold the substring. */
if (offset + length > GetLength() || dst_size <= length) {
std::abort();
}
/* Copy substring to dst. */
std::strncpy(dst, this->buffer + offset, length);
dst[length] = 0;
}
BoundedString<N> GetSubstring(size_t offset, size_t length) const {
BoundedString<N> string;
GetSubstring(string.buffer, N, offset, length);
return string;
}
/* Comparison. */
constexpr bool operator==(const BoundedString<N> &rhs) const {
return std::strncmp(this->buffer, rhs.buffer, N) == 0;
}
constexpr bool operator!=(const BoundedString<N> &rhs) const {
return !(*this == rhs);
}
bool EndsWith(const char *s, size_t offset) const {
return std::strncmp(this->buffer + offset, s, N - offset) == 0;
}
bool EndsWith(const char *s) const {
const size_t suffix_length = strnlen(s, N);
const size_t length = GetLength();
return suffix_length <= length && EndsWith(s, length - suffix_length);
}
};
}

View File

@@ -0,0 +1,422 @@
/*
* Copyright (c) 2018-2019 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 <sys/stat.h>
#include <switch.h>
#include "kvdb_bounded_string.hpp"
#include "kvdb_file_key_value_store.hpp"
namespace sts::kvdb {
namespace impl {
template<class Key, size_t Capacity>
class LruList {
private:
/* Subtypes. */
struct LruHeader {
u32 entry_count;
};
public:
static constexpr size_t BufferSize = sizeof(Key) * Capacity;
static constexpr size_t FileSize = sizeof(LruHeader) + BufferSize;
using Path = FileKeyValueStore::Path;
private:
Path file_path;
Key *keys;
LruHeader header;
public:
static Result CreateNewList(const char *path) {
/* Create new lru_list.dat. */
R_TRY(fsdevCreateFile(path, FileSize, 0));
/* Open the file. */
FILE *fp = fopen(path, "r+b");
if (fp == nullptr) {
return fsdevGetLastResult();
}
ON_SCOPE_EXIT { fclose(fp); };
/* Write new header with zero entries to the file. */
LruHeader new_header = { .entry_count = 0, };
if (fwrite(&new_header, sizeof(new_header), 1, fp) != 1) {
return fsdevGetLastResult();
}
return ResultSuccess;
}
private:
void RemoveIndex(size_t i) {
if (i >= this->GetCount()) {
std::abort();
}
std::memmove(this->keys + i, this->keys + i + 1, sizeof(*this->keys) * (this->GetCount() - (i + 1)));
this->DecrementCount();
}
void IncrementCount() {
this->header.entry_count++;
}
void DecrementCount() {
this->header.entry_count--;
}
public:
LruList() : keys(nullptr), header({}) { /* ... */ }
Result Initialize(const char *path, void *buf, size_t size) {
/* Only initialize once, and ensure we have sufficient memory. */
if (this->keys != nullptr || size < BufferSize) {
std::abort();
}
/* Setup member variables. */
this->keys = static_cast<Key *>(buf);
this->file_path.Set(path);
std::memset(this->keys, 0, BufferSize);
/* Open file. */
FILE *fp = fopen(this->file_path, "rb");
if (fp == nullptr) {
return fsdevGetLastResult();
}
ON_SCOPE_EXIT { fclose(fp); };
/* Read header. */
if (fread(&this->header, sizeof(this->header), 1, fp) != 1) {
return fsdevGetLastResult();
}
/* Read entries. */
const size_t count = this->GetCount();
if (count > 0) {
if (fread(this->keys, std::min(BufferSize, sizeof(Key) * count), 1, fp) != 1) {
return fsdevGetLastResult();
}
}
return ResultSuccess;
}
Result Save() {
/* Open file. */
FILE *fp = fopen(this->file_path, "r+b");
if (fp == nullptr) {
return fsdevGetLastResult();
}
ON_SCOPE_EXIT { fclose(fp); };
/* Write header. */
if (fwrite(&this->header, sizeof(this->header), 1, fp) != 1) {
return fsdevGetLastResult();
}
/* Write entries. */
if (fwrite(this->keys, BufferSize, 1, fp) != 1) {
return fsdevGetLastResult();
}
/* Flush. */
fflush(fp);
return ResultSuccess;
}
size_t GetCount() const {
return this->header.entry_count;
}
bool IsEmpty() const {
return this->GetCount() == 0;
}
bool IsFull() const {
return this->GetCount() >= Capacity;
}
Key Get(size_t i) const {
if (i >= this->GetCount()) {
std::abort();
}
return this->keys[i];
}
Key Peek() const {
if (this->IsEmpty()) {
std::abort();
}
return this->Get(0);
}
void Push(const Key &key) {
if (this->IsFull()) {
std::abort();
}
this->keys[this->GetCount()] = key;
this->IncrementCount();
}
Key Pop() {
if (this->IsEmpty()) {
std::abort();
}
this->RemoveIndex(0);
}
bool Remove(const Key &key) {
const size_t count = this->GetCount();
/* Iterate over the list, removing the last entry that matches the key. */
for (size_t i = 0; i < count; i++) {
if (this->keys[count - 1 - i] == key) {
this->RemoveIndex(count - 1 - i);
return true;
}
}
return false;
}
bool Contains(const Key &key) const {
const size_t count = this->GetCount();
/* Iterate over the list, checking to see if we have the key. */
for (size_t i = 0; i < count; i++) {
if (this->keys[count - 1 - i] == key) {
return true;
}
}
return false;
}
bool Update(const Key &key) {
if (this->Remove(key)) {
this->Push(key);
return true;
}
return false;
}
};
}
template<class Key, size_t Capacity>
class FileKeyValueCache {
static_assert(std::is_pod<Key>::value, "FileKeyValueCache Key must be pod!");
static_assert(sizeof(Key) <= FileKeyValueStore::MaxKeySize, "FileKeyValueCache Key is too big!");
public:
using LeastRecentlyUsedList = impl::LruList<Key, Capacity>;
/* Note: Nintendo code in NS uses Path = BoundedString<0x180> here. */
/* It's unclear why, since they use 0x300 everywhere else. */
/* We'll just use 0x300, since it shouldn't make a difference, */
/* as FileKeyValueStore paths are limited to 0x100 anyway. */
using Path = typename LeastRecentlyUsedList::Path;
private:
FileKeyValueStore kvs;
LeastRecentlyUsedList lru_list;
private:
static constexpr Path GetLeastRecentlyUsedListPath(const char *dir) {
return Path::MakeFormat("%s/%s", dir, "lru_list.dat");
}
static constexpr Path GetFileKeyValueStorePath(const char *dir) {
return Path::MakeFormat("%s/%s", dir, "kvs");
}
static Result Exists(bool *out, const char *path, bool is_dir) {
/* Check if the path exists. */
struct stat st;
if (stat(path, &st) != 0) {
R_TRY_CATCH(fsdevGetLastResult()) {
R_CATCH(ResultFsPathNotFound) {
/* If the path doesn't exist, nothing has gone wrong. */
*out = false;
return ResultSuccess;
}
} R_END_TRY_CATCH;
}
/* Check that our entry type is correct. */
if ((is_dir && !(S_ISDIR(st.st_mode))) || (!is_dir && !(S_ISREG(st.st_mode)))) {
return ResultKvdbInvalidFilesystemState;
}
*out = true;
return ResultSuccess;
}
static Result DirectoryExists(bool *out, const char *path) {
return Exists(out, path, true);
}
static Result FileExists(bool *out, const char *path) {
return Exists(out, path, false);
}
public:
static Result CreateNewCache(const char *dir) {
/* Make a new key value store filesystem, and a new lru_list.dat. */
R_TRY(LeastRecentlyUsedList::CreateNewList(GetLeastRecentlyUsedListPath(dir)));
if (mkdir(GetFileKeyValueStorePath(dir), 0) != 0) {
return fsdevGetLastResult();
}
return ResultSuccess;
}
static Result ValidateExistingCache(const char *dir) {
/* Check for existence. */
bool has_lru = false, has_kvs = false;
R_TRY(FileExists(&has_lru, GetLeastRecentlyUsedListPath(dir)));
R_TRY(DirectoryExists(&has_kvs, GetFileKeyValueStorePath(dir)));
/* If neither exists, CreateNewCache was never called. */
if (!has_lru && !has_kvs) {
return ResultKvdbNotCreated;
}
/* If one exists but not the other, we have an invalid state. */
if (has_lru ^ has_kvs) {
return ResultKvdbInvalidFilesystemState;
}
return ResultSuccess;
}
private:
void RemoveOldestKey() {
const Key &oldest_key = this->lru_list.Peek();
this->lru_list.Pop();
this->kvs.Remove(oldest_key);
}
public:
Result Initialize(const char *dir, void *buf, size_t size) {
/* Initialize list. */
R_TRY(this->lru_list.Initialize(GetLeastRecentlyUsedListPath(dir), buf, size));
/* Initialize kvs. */
/* NOTE: Despite creating the kvs folder and returning an error if it does not exist, */
/* Nintendo does not use the kvs folder, and instead uses the passed dir. */
/* This causes lru_list.dat to be in the same directory as the store's .val files */
/* instead of in the same directory as a folder containing the store's .val files. */
/* This is probably a Nintendo bug, but because system saves contain data in the wrong */
/* layout it can't really be fixed without breaking existing devices... */
R_TRY(this->kvs.Initialize(dir));
return ResultSuccess;
}
size_t GetCount() const {
return this->lru_list.GetCount();
}
size_t GetCapacity() const {
return Capacity;
}
Key GetKey(size_t i) const {
return this->lru_list.Get(i);
}
bool Contains(const Key &key) const {
return this->lru_list.Contains(key);
}
Result Get(size_t *out_size, void *out_value, size_t max_out_size, const Key &key) {
/* Note that we accessed the key. */
this->lru_list.Update(key);
return this->kvs.Get(out_size, out_value, max_out_size, key);
}
template<typename Value>
Result Get(Value *out_value, const Key &key) {
/* Note that we accessed the key. */
this->lru_list.Update(key);
return this->kvs.Get(out_value, key);
}
Result GetSize(size_t *out_size, const Key &key) {
return this->kvs.GetSize(out_size, key);
}
Result Set(const Key &key, const void *value, size_t value_size) {
if (this->lru_list.Update(key)) {
/* If an entry for the key exists, delete the existing value file. */
this->kvs.Remove(key);
} else {
/* If the list is full, we need to remove the oldest key. */
if (this->lru_list.IsFull()) {
this->RemoveOldestKey();
}
/* Add the key to the list. */
this->lru_list.Push(key);
}
/* Loop, trying to save the new value to disk. */
while (true) {
/* Try to set the key. */
R_TRY_CATCH(this->kvs.Set(key, value, value_size)) {
R_CATCH_RANGE(ResultFsNotEnoughFreeSpace) {
/* If our entry is the only thing in the Lru list, remove it. */
if (this->lru_list.GetCount() == 1) {
this->lru_list.Pop();
R_TRY(this->lru_list.Save());
return R_TRY_CATCH_RESULT;
}
/* Otherwise, remove the oldest element from the cache and try again. */
this->RemoveOldestKey();
continue;
}
} R_END_TRY_CATCH;
/* If we got here, we succeeded. */
break;
}
/* Save the list. */
R_TRY(this->lru_list.Save());
return ResultSuccess;
}
template<typename Value>
Result Set(const Key &key, const Value &value) {
return this->Set(key, &value, sizeof(Value));
}
Result Remove(const Key &key) {
/* Remove the key. */
this->lru_list.Remove(key);
R_TRY(this->kvs.Remove(key));
R_TRY(this->lru_list.Save());
return ResultSuccess;
}
Result RemoveAll() {
/* TODO: Nintendo doesn't check errors here. Should we? */
while (!this->lru_list.IsEmpty()) {
this->RemoveOldestKey();
}
R_TRY(this->lru_list.Save());
return ResultSuccess;
}
};
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright (c) 2018-2019 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 <optional>
#include <switch.h>
#include "kvdb_bounded_string.hpp"
namespace sts::kvdb {
class FileKeyValueStore {
NON_COPYABLE(FileKeyValueStore);
NON_MOVEABLE(FileKeyValueStore);
public:
static constexpr size_t MaxPathLength = 0x300; /* TODO: FS_MAX_PATH - 1? */
static constexpr size_t MaxFileLength = 0xFF;
static constexpr char FileExtension[5] = ".val";
static constexpr size_t FileExtensionLength = sizeof(FileExtension) - 1;
static constexpr size_t MaxKeySize = (MaxFileLength - FileExtensionLength) / 2;
using Path = kvdb::BoundedString<MaxPathLength>;
using FileName = kvdb::BoundedString<MaxFileLength>;
private:
/* Subtypes. */
struct Entry {
u8 key[MaxKeySize];
void *value;
size_t key_size;
size_t value_size;
};
static_assert(std::is_pod<Entry>::value, "FileKeyValueStore::Entry definition!");
class Cache {
private:
u8 *backing_buffer = nullptr;
size_t backing_buffer_size = 0;
size_t backing_buffer_free_offset = 0;
Entry *entries = nullptr;
size_t count = 0;
size_t capacity = 0;
private:
void *Allocate(size_t size);
bool HasEntries() const {
return this->entries != nullptr && this->capacity != 0;
}
public:
Result Initialize(void *buffer, size_t buffer_size, size_t capacity);
void Invalidate();
std::optional<size_t> TryGet(void *out_value, size_t max_out_size, const void *key, size_t key_size);
std::optional<size_t> TryGetSize(const void *key, size_t key_size);
void Set(const void *key, size_t key_size, const void *value, size_t value_size);
bool Contains(const void *key, size_t key_size);
};
private:
HosMutex lock;
Path dir_path;
Cache cache;
private:
Path GetPath(const void *key, size_t key_size);
Result GetKey(size_t *out_size, void *out_key, size_t max_out_size, const FileName &file_name);
public:
FileKeyValueStore() { /* ... */ }
/* Basic accessors. */
Result Initialize(const char *dir);
Result InitializeWithCache(const char *dir, void *cache_buffer, size_t cache_buffer_size, size_t cache_capacity);
Result Get(size_t *out_size, void *out_value, size_t max_out_size, const void *key, size_t key_size);
Result GetSize(size_t *out_size, const void *key, size_t key_size);
Result Set(const void *key, size_t key_size, const void *value, size_t value_size);
Result Remove(const void *key, size_t key_size);
/* Niceties. */
template<typename Key>
Result Get(size_t *out_size, void *out_value, size_t max_out_size, const Key &key) {
static_assert(std::is_pod<Key>::value && sizeof(Key) <= MaxKeySize, "Invalid FileKeyValueStore Key!");
return this->Get(out_size, out_value, max_out_size, &key, sizeof(Key));
}
template<typename Key, typename Value>
Result Get(Value *out_value, const Key &key) {
static_assert(std::is_pod<Value>::value && !std::is_pointer<Value>::value, "Invalid FileKeyValueStore Value!");
size_t size = 0;
R_TRY(this->Get(&size, out_value, sizeof(Value), key));
if (size < sizeof(Value)) {
std::abort();
}
return ResultSuccess;
}
template<typename Key>
Result GetSize(size_t *out_size, const Key &key) {
return this->GetSize(out_size, &key, sizeof(Key));
}
template<typename Key>
Result Set(const Key &key, const void *value, size_t value_size) {
static_assert(std::is_pod<Key>::value && sizeof(Key) <= MaxKeySize, "Invalid FileKeyValueStore Key!");
return this->Set(&key, sizeof(Key), value, value_size);
}
template<typename Key, typename Value>
Result Set(const Key &key, const Value &value) {
static_assert(std::is_pod<Value>::value && !std::is_pointer<Value>::value, "Invalid FileKeyValueStore Value!");
return this->Set(key, &value, sizeof(Value));
}
template<typename Key>
Result Remove(const Key &key) {
return this->Remove(&key, sizeof(Key));
}
};
}

View File

@@ -0,0 +1,569 @@
/*
* Copyright (c) 2018-2019 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 <algorithm>
#include <switch.h>
#include <sys/stat.h>
#include "kvdb_auto_buffer.hpp"
#include "kvdb_archive.hpp"
#include "kvdb_bounded_string.hpp"
#include "kvdb_memory_key_value_store.hpp"
namespace sts::kvdb {
template<class Key>
class MemoryKeyValueStore {
static_assert(std::is_pod<Key>::value, "KeyValueStore Keys must be pod!");
NON_COPYABLE(MemoryKeyValueStore);
NON_MOVEABLE(MemoryKeyValueStore);
public:
/* Subtypes. */
class Entry {
private:
Key key;
void *value;
size_t value_size;
public:
constexpr Entry(const Key &k, void *v, size_t s) : key(k), value(v), value_size(s) { /* ... */ }
const Key &GetKey() const {
return this->key;
}
template<typename Value = void>
Value *GetValuePointer() {
/* Size check. Note: Nintendo does not size check. */
if constexpr (!std::is_same<Value, void>::value) {
if (sizeof(Value) > this->value_size) {
std::abort();
}
/* Ensure we only get pod. */
static_assert(std::is_pod<Value>::value, "KeyValueStore Values must be pod");
}
return reinterpret_cast<Value *>(this->value);
}
template<typename Value = void>
const Value *GetValuePointer() const {
/* Size check. Note: Nintendo does not size check. */
if constexpr (!std::is_same<Value, void>::value) {
if (sizeof(Value) > this->value_size) {
std::abort();
}
/* Ensure we only get pod. */
static_assert(std::is_pod<Value>::value, "KeyValueStore Values must be pod");
}
return reinterpret_cast<Value *>(this->value);
}
template<typename Value>
Value &GetValue() {
return *(this->GetValuePointer<Value>());
}
template<typename Value>
const Value &GetValue() const {
return *(this->GetValuePointer<Value>());
}
size_t GetValueSize() const {
return this->value_size;
}
constexpr inline bool operator<(const Key &rhs) const {
return key < rhs;
}
constexpr inline bool operator==(const Key &rhs) const {
return key == rhs;
}
};
class Index {
private:
size_t count;
size_t capacity;
Entry *entries;
public:
Index() : count(0), capacity(0), entries(nullptr) { /* ... */ }
~Index() {
if (this->entries != nullptr) {
this->ResetEntries();
std::free(this->entries);
this->entries = nullptr;
}
}
size_t GetCount() const {
return this->count;
}
size_t GetCapacity() const {
return this->capacity;
}
void ResetEntries() {
for (size_t i = 0; i < this->count; i++) {
std::free(this->entries[i].GetValuePointer());
}
this->count = 0;
}
Result Initialize(size_t capacity) {
this->entries = reinterpret_cast<Entry *>(std::malloc(sizeof(Entry) * capacity));
if (this->entries == nullptr) {
return ResultKvdbAllocationFailed;
}
this->capacity = capacity;
return ResultSuccess;
}
Result Set(const Key &key, const void *value, size_t value_size) {
/* Allocate new value. */
void *new_value = std::malloc(value_size);
if (new_value == nullptr) {
return ResultKvdbAllocationFailed;
}
std::memcpy(new_value, value, value_size);
/* Find entry for key. */
Entry *it = this->lower_bound(key);
if (it != this->end() && it->GetKey() == key) {
/* Entry already exists. Free old value. */
std::free(it->GetValuePointer());
} else {
/* We need to add a new entry. Check we have room, move future keys forward. */
if (this->count >= this->capacity) {
std::free(new_value);
return ResultKvdbKeyCapacityInsufficient;
}
std::memmove(it + 1, it, sizeof(*it) * (this->end() - it));
this->count++;
}
/* Save the new Entry in the map. */
*it = Entry(key, new_value, value_size);
return ResultSuccess;
}
Result AddUnsafe(const Key &key, void *value, size_t value_size) {
if (this->count >= this->capacity) {
return ResultKvdbKeyCapacityInsufficient;
}
this->entries[this->count++] = Entry(key, value, value_size);
return ResultSuccess;
}
Result Remove(const Key &key) {
/* Find entry for key. */
Entry *it = this->find(key);
/* Check if the entry is valid. */
if (it != this->end()) {
/* Free the value, move entries back. */
std::free(it->GetValuePointer());
std::memmove(it, it + 1, sizeof(*it) * (this->end() - (it + 1)));
this->count--;
return ResultSuccess;
}
/* If it's not, we didn't remove it. */
return ResultKvdbKeyNotFound;
}
Entry *begin() {
return this->GetBegin();
}
const Entry *begin() const {
return this->GetBegin();
}
Entry *end() {
return this->GetEnd();
}
const Entry *end() const {
return this->GetEnd();
}
const Entry *cbegin() const {
return this->begin();
}
const Entry *cend() const {
return this->end();
}
Entry *lower_bound(const Key &key) {
return this->GetLowerBound(key);
}
const Entry *lower_bound(const Key &key) const {
return this->GetLowerBound(key);
}
Entry *find(const Key &key) {
return this->Find(key);
}
const Entry *find(const Key &key) const {
return this->Find(key);
}
private:
Entry *GetBegin() {
return this->entries;
}
const Entry *GetBegin() const {
return this->entries;
}
Entry *GetEnd() {
return this->GetBegin() + this->count;
}
const Entry *GetEnd() const {
return this->GetBegin() + this->count;
}
Entry *GetLowerBound(const Key &key) {
return std::lower_bound(this->GetBegin(), this->GetEnd(), key);
}
const Entry *GetLowerBound(const Key &key) const {
return std::lower_bound(this->GetBegin(), this->GetEnd(), key);
}
Entry *Find(const Key &key) {
auto it = this->GetLowerBound(key);
auto end = this->GetEnd();
if (it != end && it->GetKey() == key) {
return it;
}
return end;
}
const Entry *Find(const Key &key) const {
auto it = this->GetLowerBound(key);
auto end = this->GetEnd();
if (it != end && it->GetKey() == key) {
return it;
}
return end;
}
};
private:
static constexpr size_t MaxPathLen = 0x300; /* TODO: FS_MAX_PATH - 1? */
using Path = kvdb::BoundedString<MaxPathLen>;
private:
Index index;
Path path;
Path temp_path;
public:
MemoryKeyValueStore() { /* ... */ }
Result Initialize(const char *dir, size_t capacity) {
/* Ensure that the passed path is a directory. */
{
struct stat st;
if (stat(dir, &st) != 0 || !(S_ISDIR(st.st_mode))) {
return ResultFsPathNotFound;
}
}
/* Set paths. */
this->path.SetFormat("%s%s", dir, "/imkvdb.arc");
this->temp_path.SetFormat("%s%s", dir, "/imkvdb.tmp");
/* Initialize our index. */
R_TRY(this->index.Initialize(capacity));
return ResultSuccess;
}
Result Initialize(size_t capacity) {
/* This initializes without an archive file. */
/* A store initialized this way cannot have its contents loaded from or flushed to disk. */
this->path.Set("");
this->temp_path.Set("");
/* Initialize our index. */
R_TRY(this->index.Initialize(capacity));
return ResultSuccess;
}
size_t GetCount() const {
return this->index.GetCount();
}
size_t GetCapacity() const {
return this->index.GetCapacity();
}
Result Load() {
/* Reset any existing entries. */
this->index.ResetEntries();
/* Try to read the archive -- note, path not found is a success condition. */
/* This is because no archive file = no entries, so we're in the right state. */
AutoBuffer buffer;
R_TRY_CATCH(this->ReadArchiveFile(&buffer)) {
R_CATCH(ResultFsPathNotFound) {
return ResultSuccess;
}
} R_END_TRY_CATCH;
/* Parse entries from the buffer. */
{
ArchiveReader reader(buffer);
size_t entry_count = 0;
R_TRY(reader.ReadEntryCount(&entry_count));
for (size_t i = 0; i < entry_count; i++) {
/* Get size of key/value. */
size_t key_size = 0, value_size = 0;
R_TRY(reader.GetEntrySize(&key_size, &value_size));
/* Allocate memory for value. */
void *new_value = std::malloc(value_size);
if (new_value == nullptr) {
return ResultKvdbAllocationFailed;
}
auto value_guard = SCOPE_GUARD { std::free(new_value); };
/* Read key and value. */
Key key;
R_TRY(reader.ReadEntry(&key, sizeof(key), new_value, value_size));
R_TRY(this->index.AddUnsafe(key, new_value, value_size));
/* We succeeded, so cancel the value guard to prevent deallocation. */
value_guard.Cancel();
}
}
return ResultSuccess;
}
Result Save() {
/* Create a buffer to hold the archive. */
AutoBuffer buffer;
R_TRY(buffer.Initialize(this->GetArchiveSize()));
/* Write the archive to the buffer. */
{
ArchiveWriter writer(buffer);
writer.WriteHeader(this->GetCount());
for (const auto &it : this->index) {
const auto &key = it.GetKey();
writer.WriteEntry(&key, sizeof(Key), it.GetValuePointer(), it.GetValueSize());
}
}
/* Save the buffer to disk. */
return this->Commit(buffer);
}
Result Set(const Key &key, const void *value, size_t value_size) {
return this->index.Set(key, value, value_size);
}
template<typename Value>
Result Set(const Key &key, const Value &value) {
/* Only allow setting pod. */
static_assert(std::is_pod<Value>::value, "KeyValueStore Values must be pod");
return this->Set(key, &value, sizeof(Value));
}
template<typename Value>
Result Set(const Key &key, const Value *value) {
/* Only allow setting pod. */
static_assert(std::is_pod<Value>::value, "KeyValueStore Values must be pod");
return this->Set(key, value, sizeof(Value));
}
Result Get(size_t *out_size, void *out_value, size_t max_out_size, const Key &key) {
/* Find entry. */
auto it = this->find(key);
if (it == this->end()) {
return ResultKvdbKeyNotFound;
}
size_t size = std::min(max_out_size, it.GetValueSize());
std::memcpy(out_value, it.GetValuePointer(), size);
*out_size = size;
return ResultSuccess;
}
template<typename Value = void>
Result GetValuePointer(Value **out_value, const Key &key) {
/* Find entry. */
auto it = this->find(key);
if (it == this->end()) {
return ResultKvdbKeyNotFound;
}
*out_value = it->template GetValuePointer<Value>();
return ResultSuccess;
}
template<typename Value = void>
Result GetValuePointer(const Value **out_value, const Key &key) const {
/* Find entry. */
auto it = this->find(key);
if (it == this->end()) {
return ResultKvdbKeyNotFound;
}
*out_value = it->template GetValuePointer<Value>();
return ResultSuccess;
}
template<typename Value>
Result GetValue(Value *out_value, const Key &key) const {
/* Find entry. */
auto it = this->find(key);
if (it == this->end()) {
return ResultKvdbKeyNotFound;
}
*out_value = it->template GetValue<Value>();
return ResultSuccess;
}
Result GetValueSize(size_t *out_size, const Key &key) const {
/* Find entry. */
auto it = this->find(key);
if (it == this->end()) {
return ResultKvdbKeyNotFound;
}
*out_size = it->GetValueSize();
return ResultSuccess;
}
Result Remove(const Key &key) {
return this->index.Remove(key);
}
Entry *begin() {
return this->index.begin();
}
const Entry *begin() const {
return this->index.begin();
}
Entry *end() {
return this->index.end();
}
const Entry *end() const {
return this->index.end();
}
const Entry *cbegin() const {
return this->index.cbegin();
}
const Entry *cend() const {
return this->index.cend();
}
Entry *lower_bound(const Key &key) {
return this->index.lower_bound(key);
}
const Entry *lower_bound(const Key &key) const {
return this->index.lower_bound(key);
}
Entry *find(const Key &key) {
return this->index.find(key);
}
const Entry *find(const Key &key) const {
return this->index.find(key);
}
private:
Result Commit(const AutoBuffer &buffer) {
/* Try to delete temporary archive, but allow deletion failure (it may not exist). */
std::remove(this->temp_path.Get());
/* Create new temporary archive. */
R_TRY(fsdevCreateFile(this->temp_path.Get(), buffer.GetSize(), 0));
/* Write data to the temporary archive. */
{
FILE *f = fopen(this->temp_path, "r+b");
if (f == nullptr) {
return fsdevGetLastResult();
}
ON_SCOPE_EXIT { fclose(f); };
if (fwrite(buffer.Get(), buffer.GetSize(), 1, f) != 1) {
return fsdevGetLastResult();
}
}
/* Try to delete the saved archive, but allow deletion failure. */
std::remove(this->path.Get());
/* Rename the path. */
if (std::rename(this->temp_path.Get(), this->path.Get()) != 0) {
return fsdevGetLastResult();
}
return ResultSuccess;
}
size_t GetArchiveSize() const {
ArchiveSizeHelper size_helper;
for (const auto &it : this->index) {
size_helper.AddEntry(sizeof(Key), it.GetValueSize());
}
return size_helper.GetSize();
}
Result ReadArchiveFile(AutoBuffer *dst) const {
/* Open the file. */
FILE *f = fopen(this->path, "rb");
if (f == nullptr) {
return fsdevGetLastResult();
}
ON_SCOPE_EXIT { fclose(f); };
/* Get the archive file size. */
fseek(f, 0, SEEK_END);
const size_t archive_size = ftell(f);
fseek(f, 0, SEEK_SET);
/* Make a new buffer, read the file. */
R_TRY(dst->Initialize(archive_size));
if (fread(dst->Get(), archive_size, 1, f) != 1) {
return fsdevGetLastResult();
}
return ResultSuccess;
}
};
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "ldr/ldr_types.hpp"

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2019 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 "ldr_types.hpp"
namespace sts::ldr::pm {
/* Process Manager API. */
Result CreateProcess(Handle *out, PinId pin_id, u32 flags, Handle reslimit);
Result GetProgramInfo(ProgramInfo *out, const ncm::TitleLocation &loc);
Result PinTitle(PinId *out, const ncm::TitleLocation &loc);
Result UnpinTitle(PinId pin_id);
Result HasLaunchedTitle(bool *out, ncm::TitleId title_id);
}

View File

@@ -0,0 +1,244 @@
/*
* Copyright (c) 2018-2019 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 <type_traits>
#include <switch.h>
#include "../svc/svc_types.hpp"
#include "../ncm/ncm_types.hpp"
namespace sts::ldr {
/* General types. */
struct ProgramInfo {
u8 main_thread_priority;
u8 default_cpu_id;
u16 flags;
u32 main_thread_stack_size;
ncm::TitleId title_id;
u32 acid_sac_size;
u32 aci_sac_size;
u32 acid_fac_size;
u32 aci_fah_size;
u8 ac_buffer[0x3E0];
};
static_assert(sizeof(ProgramInfo) == 0x400, "ProgramInfo definition!");
enum ProgramInfoFlag {
ProgramInfoFlag_SystemModule = (0 << 0),
ProgramInfoFlag_Application = (1 << 0),
ProgramInfoFlag_Applet = (2 << 0),
ProgramInfoFlag_InvalidType = (3 << 0),
ProgramInfoFlag_ApplicationTypeMask = (3 << 0),
ProgramInfoFlag_AllowDebug = (1 << 2),
};
enum CreateProcessFlag {
CreateProcessFlag_EnableDebug = (1 << 0),
CreateProcessFlag_DisableAslr = (1 << 1),
};
struct ProgramArguments {
u32 allocated_size;
u32 arguments_size;
u8 reserved[0x18];
u8 arguments[];
};
static_assert(sizeof(ProgramArguments) == 0x20, "ProgramArguments definition!");
struct PinId {
u64 value;
};
inline bool operator==(const PinId &lhs, const PinId &rhs) {
return lhs.value == rhs.value;
}
inline bool operator!=(const PinId &lhs, const PinId &rhs) {
return lhs.value != rhs.value;
}
static_assert(sizeof(PinId) == sizeof(u64) && std::is_pod<PinId>::value, "PinId definition!");
/* Import ModuleInfo from libnx. */
using ModuleInfo = ::LoaderModuleInfo;
/* NSO types. */
struct NsoHeader {
static constexpr u32 Magic = 0x304F534E;
enum Segment : size_t {
Segment_Text = 0,
Segment_Ro = 1,
Segment_Rw = 2,
Segment_Count,
};
enum Flag : u32 {
Flag_CompressedText = (1 << 0),
Flag_CompressedRo = (1 << 1),
Flag_CompressedRw = (1 << 2),
Flag_CheckHashText = (1 << 3),
Flag_CheckHashRo = (1 << 4),
Flag_CheckHashRw = (1 << 5),
};
struct SegmentInfo {
u32 file_offset;
u32 dst_offset;
u32 size;
u32 reserved;
};
u32 magic;
u32 version;
u32 reserved_08;
u32 flags;
union {
struct {
u32 text_file_offset;
u32 text_dst_offset;
u32 text_size;
u32 unk_file_offset;
u32 ro_file_offset;
u32 ro_dst_offset;
u32 ro_size;
u32 unk_size;
u32 rw_file_offset;
u32 rw_dst_offset;
u32 rw_size;
u32 bss_size;
};
SegmentInfo segments[Segment_Count];
};
u8 build_id[sizeof(ModuleInfo::build_id)];
union {
u32 compressed_sizes[Segment_Count];
struct {
u32 text_compressed_size;
u32 ro_compressed_size;
u32 rw_compressed_size;
};
};
u8 reserved_6C[0x34];
union {
u8 segment_hashes[Segment_Count][SHA256_HASH_SIZE];
struct {
u8 text_hash[SHA256_HASH_SIZE];
u8 ro_hash[SHA256_HASH_SIZE];
u8 rw_hash[SHA256_HASH_SIZE];
};
};
};
static_assert(sizeof(NsoHeader) == 0x100 && std::is_pod<NsoHeader>::value, "NsoHeader definition!");
/* NPDM types. */
struct Aci {
static constexpr u32 Magic = 0x30494341;
u32 magic;
u8 reserved_04[0xC];
ncm::TitleId title_id;
u8 reserved_18[0x8];
u32 fah_offset;
u32 fah_size;
u32 sac_offset;
u32 sac_size;
u32 kac_offset;
u32 kac_size;
u8 reserved_38[0x8];
};
static_assert(sizeof(Aci) == 0x40 && std::is_pod<Aci>::value, "Aci definition!");
struct Acid {
static constexpr u32 Magic = 0x44494341;
enum AcidFlag {
AcidFlag_Production = (1 << 0),
AcidFlag_UnqualifiedApproval = (1 << 1),
AcidFlag_DeprecatedUseSecureMemory = (1 << 2),
AcidFlag_PoolPartitionShift = 2,
AcidFlag_PoolPartitionMask = (3 << AcidFlag_PoolPartitionShift),
};
enum PoolPartition {
PoolPartition_Application = (svc::CreateProcessFlag_PoolPartitionApplication >> svc::CreateProcessFlag_PoolPartitionShift),
PoolPartition_Applet = (svc::CreateProcessFlag_PoolPartitionApplet >> svc::CreateProcessFlag_PoolPartitionShift),
PoolPartition_System = (svc::CreateProcessFlag_PoolPartitionSystem >> svc::CreateProcessFlag_PoolPartitionShift),
PoolPartition_SystemNonSecure = (svc::CreateProcessFlag_PoolPartitionSystemNonSecure >> svc::CreateProcessFlag_PoolPartitionShift),
};
u8 signature[0x100];
u8 modulus[0x100];
u32 magic;
u32 size;
u8 version;
u8 reserved_209[3];
u32 flags;
ncm::TitleId title_id_min;
ncm::TitleId title_id_max;
u32 fac_offset;
u32 fac_size;
u32 sac_offset;
u32 sac_size;
u32 kac_offset;
u32 kac_size;
u8 reserved_238[0x8];
};
static_assert(sizeof(Acid) == 0x240 && std::is_pod<Acid>::value, "Acid definition!");
struct Npdm {
static constexpr u32 Magic = 0x4154454D;
enum MetaFlag {
MetaFlag_Is64Bit = (1 << 0),
MetaFlag_AddressSpaceTypeShift = 1,
MetaFlag_AddressSpaceTypeMask = (7 << MetaFlag_AddressSpaceTypeShift),
MetaFlag_OptimizeMemoryAllocation = (1 << 4),
};
enum AddressSpaceType {
AddressSpaceType_32Bit = (svc::CreateProcessFlag_AddressSpace32Bit >> svc::CreateProcessFlag_AddressSpaceShift),
AddressSpaceType_64BitDeprecated = (svc::CreateProcessFlag_AddressSpace64BitDeprecated >> svc::CreateProcessFlag_AddressSpaceShift),
AddressSpaceType_32BitWithoutAlias = (svc::CreateProcessFlag_AddressSpace32BitWithoutAlias >> svc::CreateProcessFlag_AddressSpaceShift),
AddressSpaceType_64Bit = (svc::CreateProcessFlag_AddressSpace64Bit >> svc::CreateProcessFlag_AddressSpaceShift),
};
u32 magic;
u8 reserved_04[8];
u8 flags;
u8 reserved_0D;
u8 main_thread_priority;
u8 default_cpu_id;
u8 reserved_10[4];
u32 system_resource_size;
u32 version;
u32 main_thread_stack_size;
char title_name[0x10];
char product_code[0x10];
u8 reserved_40[0x30];
u32 aci_offset;
u32 aci_size;
u32 acid_offset;
u32 acid_size;
};
static_assert(sizeof(Npdm) == 0x80 && std::is_pod<Npdm>::value, "Npdm definition!");
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "map/map_types.hpp"
#include "map/map_api.hpp"

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "map_types.hpp"
namespace sts::map {
/* Public API. */
Result GetProcessAddressSpaceInfo(AddressSpaceInfo *out, Handle process_h);
Result LocateMappableSpace(uintptr_t *out_address, size_t size);
Result MapCodeMemoryInProcess(MappedCodeMemory &out_mcm, Handle process_handle, uintptr_t base_address, size_t size);
bool CanAddGuardRegionsInProcess(Handle process_handle, uintptr_t address, size_t size);
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "../results.hpp"
namespace sts::map {
/* Types. */
struct AddressSpaceInfo {
uintptr_t heap_base;
size_t heap_size;
uintptr_t heap_end;
uintptr_t alias_base;
size_t alias_size;
uintptr_t alias_end;
uintptr_t aslr_base;
size_t aslr_size;
uintptr_t aslr_end;
};
static constexpr uintptr_t AslrBase32Bit = 0x0000200000ul;
static constexpr size_t AslrSize32Bit = 0x003FE00000ul;
static constexpr uintptr_t AslrBase64BitDeprecated = 0x0008000000ul;
static constexpr size_t AslrSize64BitDeprecated = 0x0078000000ul;
static constexpr uintptr_t AslrBase64Bit = 0x0008000000ul;
static constexpr size_t AslrSize64Bit = 0x7FF8000000ul;
class AutoCloseMap {
private:
Handle process_handle;
Result result;
void *mapped_address;
uintptr_t base_address;
size_t size;
public:
AutoCloseMap(uintptr_t mp, Handle p_h, uintptr_t ba, size_t sz) : process_handle(p_h), mapped_address(reinterpret_cast<void *>(mp)), base_address(ba), size(sz) {
this->result = svcMapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size);
}
~AutoCloseMap() {
if (this->process_handle != INVALID_HANDLE && R_SUCCEEDED(this->result)) {
R_ASSERT(svcUnmapProcessMemory(this->mapped_address, this->process_handle, this->base_address, this->size));
}
}
Result GetResult() const {
return this->result;
}
bool IsSuccess() const {
return R_SUCCEEDED(this->result);
}
void Invalidate() {
this->process_handle = INVALID_HANDLE;
}
};
class MappedCodeMemory {
private:
Handle process_handle;
Result result;
uintptr_t dst_address;
uintptr_t src_address;
size_t size;
public:
MappedCodeMemory(Result init_res) : process_handle(INVALID_HANDLE), result(init_res), dst_address(0), src_address(0), size(0) {
/* ... */
}
MappedCodeMemory(Handle p_h, uintptr_t dst, uintptr_t src, size_t sz) : process_handle(p_h), dst_address(dst), src_address(src), size(sz) {
this->result = svcMapProcessCodeMemory(this->process_handle, this->dst_address, this->src_address, this->size);
}
~MappedCodeMemory() {
if (this->process_handle != INVALID_HANDLE && R_SUCCEEDED(this->result) && this->size > 0) {
R_ASSERT(svcUnmapProcessCodeMemory(this->process_handle, this->dst_address, this->src_address, this->size));
}
}
uintptr_t GetDstAddress() const {
return this->dst_address;
}
Result GetResult() const {
return this->result;
}
bool IsSuccess() const {
return R_SUCCEEDED(this->result);
}
void Invalidate() {
this->process_handle = INVALID_HANDLE;
}
MappedCodeMemory &operator=(MappedCodeMemory &&o) {
this->process_handle = o.process_handle;
this->result = o.result;
this->dst_address = o.dst_address;
this->src_address = o.src_address;
this->size = o.size;
o.Invalidate();
return *this;
}
};
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "hossynch.hpp"
#include <memory>
class HosMessageQueue {
private:
HosMutex queue_lock;
HosCondVar cv_not_full;
HosCondVar cv_not_empty;
std::unique_ptr<uintptr_t[]> buffer;
size_t capacity;
size_t count = 0;
size_t offset = 0;
public:
HosMessageQueue(size_t c) : capacity(c) {
this->buffer = std::make_unique<uintptr_t[]>(this->capacity);
}
HosMessageQueue(std::unique_ptr<uintptr_t[]> buf, size_t c) : buffer(std::move(buf)), capacity(c) { }
/* Sending (FIFO functionality) */
void Send(uintptr_t data);
bool TrySend(uintptr_t data);
bool TimedSend(uintptr_t data, u64 timeout);
/* Sending (LIFO functionality) */
void SendNext(uintptr_t data);
bool TrySendNext(uintptr_t data);
bool TimedSendNext(uintptr_t data, u64 timeout);
/* Receive functionality */
void Receive(uintptr_t *out);
bool TryReceive(uintptr_t *out);
bool TimedReceive(uintptr_t *out, u64 timeout);
/* Peek functionality */
void Peek(uintptr_t *out);
bool TryPeek(uintptr_t *out);
bool TimedPeek(uintptr_t *out, u64 timeout);
private:
bool IsFull() {
return this->count >= this->capacity;
}
bool IsEmpty() {
return this->count == 0;
}
void SendInternal(uintptr_t data);
void SendNextInternal(uintptr_t data);
uintptr_t ReceiveInternal();
uintptr_t PeekInternal();
};

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2019 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 "sm.hpp"
#include "ipc.hpp"
#include "mitm/imitmserviceobject.hpp"
#include "mitm/mitm_query_service.hpp"
#include "mitm/mitm_session.hpp"
#include "mitm/mitm_server.hpp"

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include <atomic>
#include <stratosphere.hpp>
#include "../ncm.hpp"
#include "mitm_query_service.hpp"
class IMitmServiceObject : public IServiceObject {
protected:
std::shared_ptr<Service> forward_service;
u64 process_id;
sts::ncm::TitleId title_id;
public:
IMitmServiceObject(std::shared_ptr<Service> s, u64 pid, sts::ncm::TitleId tid) : forward_service(s), process_id(pid), title_id(tid) { /* ... */ }
virtual sts::ncm::TitleId GetTitleId() const {
return this->title_id;
}
virtual u64 GetProcessId() const {
return this->process_id;
}
virtual bool IsMitmObject() const override { return true; }
static bool ShouldMitm(u64 pid, sts::ncm::TitleId tid);
protected:
virtual ~IMitmServiceObject() = default;
};

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include <stratosphere.hpp>
#include "../ncm.hpp"
template <typename T>
class MitmQueryService : public IServiceObject {
private:
enum class CommandId {
ShouldMitm = 65000,
};
protected:
void ShouldMitm(Out<bool> should_mitm, u64 process_id, sts::ncm::TitleId title_id) {
should_mitm.SetValue(T::ShouldMitm(process_id, title_id));
}
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(MitmQueryService<T>, ShouldMitm),
};
};

View File

@@ -0,0 +1,91 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "mitm_query_service.hpp"
#include "mitm_session.hpp"
#include "../utilities.hpp"
void RegisterMitmServerQueryHandle(Handle query_h, ServiceObjectHolder &&service);
template <typename T, auto MakeShared>
class MitmServer : public IWaitable {
static_assert(std::is_base_of<IMitmServiceObject, T>::value, "MitM Service Objects must derive from IMitmServiceObject");
private:
Handle port_handle;
unsigned int max_sessions;
sts::sm::ServiceName mitm_name;
public:
MitmServer(const char *service_name, unsigned int max_s) : port_handle(0), max_sessions(max_s), mitm_name(sts::sm::ServiceName::Encode(service_name)) {
Handle query_h = INVALID_HANDLE;
R_ASSERT(sts::sm::mitm::InstallMitm(&this->port_handle, &query_h, this->mitm_name));
RegisterMitmServerQueryHandle(query_h, std::move(ServiceObjectHolder(std::move(std::make_shared<MitmQueryService<T>>()))));
}
virtual ~MitmServer() override {
if (this->port_handle) {
R_ASSERT(sts::sm::mitm::UninstallMitm(this->mitm_name));
svcCloseHandle(port_handle);
}
}
SessionManagerBase *GetSessionManager() {
return static_cast<SessionManagerBase *>(this->GetManager());
}
/* IWaitable */
virtual Handle GetHandle() override {
return this->port_handle;
}
virtual Result HandleSignaled(u64 timeout) override {
/* If this server's port was signaled, accept a new session. */
Handle session_h;
R_TRY(svcAcceptSession(&session_h, this->port_handle));
/* Create a forward service for this instance. */
std::shared_ptr<Service> forward_service(new Service(), [](Service *s) {
/* Custom deleter to ensure service is open as long as necessary. */
serviceClose(s);
delete s;
});
u64 client_pid;
sts::ncm::TitleId client_tid;
R_ASSERT(sts::sm::mitm::AcknowledgeSession(forward_service.get(), &client_pid, &client_tid, this->mitm_name));
this->GetSessionManager()->AddWaitable(new MitmSession(session_h, client_pid, forward_service, MakeShared(forward_service, client_pid, client_tid)));
return ResultSuccess;
}
};
template<typename T>
struct MakeSharedMitmHelper {
static constexpr std::shared_ptr<T> Make(std::shared_ptr<Service> forward_srv, u64 client_pid, sts::ncm::TitleId client_tid) {
return std::make_shared<T>(forward_srv, client_pid, client_tid);
}
};
template<typename T, auto MakeShared = MakeSharedMitmHelper<T>::Make>
static void AddMitmServerToManager(SessionManagerBase *manager, const char *srv_name, unsigned int max_sessions) {
auto *srv = new MitmServer<T, MakeShared>(srv_name, max_sessions);
manager->AddWaitable(srv);
}

View File

@@ -0,0 +1,342 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include <stratosphere.hpp>
#include "imitmserviceobject.hpp"
#include "../results.hpp"
#include "mitm_query_service.hpp"
class MitmSession final : public ServiceSession {
private:
/* This will be for the actual session. */
std::shared_ptr<Service> forward_service;
struct PostProcessHandlerContext {
bool closed;
void (*handler)(IMitmServiceObject *, IpcResponseContext *);
};
/* Store a handler for the service. */
std::shared_ptr<PostProcessHandlerContext> service_post_process_ctx;
/* For cleanup usage. */
u64 client_pid;
u32 num_fwd_copy_hnds = 0;
Handle fwd_copy_hnds[8];
public:
template<typename T>
MitmSession(Handle s_h, u64 pid, std::shared_ptr<Service> fs, std::shared_ptr<T> srv) : ServiceSession(s_h), client_pid(pid) {
this->forward_service = std::move(fs);
this->obj_holder = std::move(ServiceObjectHolder(std::move(srv)));
this->service_post_process_ctx = std::make_shared<PostProcessHandlerContext>();
this->service_post_process_ctx->closed = false;
this->service_post_process_ctx->handler = T::PostProcess;
size_t pbs;
R_ASSERT(ipcQueryPointerBufferSize(forward_service->handle, &pbs));
this->pointer_buffer.resize(pbs);
this->control_holder.Reset();
this->control_holder = std::move(ServiceObjectHolder(std::move(std::make_shared<IMitmHipcControlService>(this))));
}
MitmSession(Handle s_h, u64 pid, std::shared_ptr<Service> fs, ServiceObjectHolder &&h, std::shared_ptr<PostProcessHandlerContext> ppc) : ServiceSession(s_h), client_pid(pid) {
this->session_handle = s_h;
this->forward_service = std::move(fs);
this->obj_holder = std::move(h);
this->service_post_process_ctx = ppc;
size_t pbs;
R_ASSERT(ipcQueryPointerBufferSize(forward_service->handle, &pbs));
this->pointer_buffer.resize(pbs);
this->control_holder.Reset();
this->control_holder = std::move(ServiceObjectHolder(std::move(std::make_shared<IMitmHipcControlService>(this))));
}
virtual void PreProcessRequest(IpcResponseContext *ctx) override {
u32 *cmdbuf = (u32 *)armGetTls();
u32 *backup_cmdbuf = (u32 *)this->backup_tls;
if (ctx->request.HasPid) {
/* [ctrl 0] [ctrl 1] [handle desc 0] [pid low] [pid high] */
cmdbuf[4] = 0xFFFE0000UL | (cmdbuf[4] & 0xFFFFUL);
backup_cmdbuf[4] = cmdbuf[4];
}
}
Result ForwardRequest(IpcResponseContext *ctx) {
/* Dispatch forwards. */
R_TRY(serviceIpcDispatch(this->forward_service.get()));
/* Parse. */
{
IpcParsedCommand r;
if (ctx->request.IsDomainRequest) {
/* We never work with out object ids, so this should be fine. */
ipcParseDomainResponse(&r, 0);
} else {
ipcParse(&r);
}
struct {
u64 magic;
u64 result;
} *resp = (decltype(resp))r.Raw;
for (unsigned int i = 0; i < r.NumHandles; i++) {
if (r.WasHandleCopied[i]) {
this->fwd_copy_hnds[num_fwd_copy_hnds++] = r.Handles[i];
}
}
R_TRY(resp->result);
}
return ResultSuccess;
}
virtual Result GetResponse(IpcResponseContext *ctx) {
FirmwareVersion fw = GetRuntimeFirmwareVersion();
const ServiceCommandMeta *dispatch_table = ctx->obj_holder->GetDispatchTable();
size_t entry_count = ctx->obj_holder->GetDispatchTableEntryCount();
if (IsDomainObject(ctx->obj_holder)) {
switch (ctx->request.InMessageType) {
case DomainMessageType_Invalid:
return ResultKernelConnectionClosed;
case DomainMessageType_Close:
{
auto sub_obj = ctx->obj_holder->GetServiceObject<IDomainObject>()->GetObject(ctx->request.InThisObjectId);
if (sub_obj == nullptr) {
R_TRY(ForwardRequest(ctx));
return ResultSuccess;
}
if (sub_obj->IsMitmObject()) {
R_TRY(ForwardRequest(ctx));
ctx->obj_holder->GetServiceObject<IDomainObject>()->FreeObject(ctx->request.InThisObjectId);
} else {
R_TRY(ctx->obj_holder->GetServiceObject<IDomainObject>()->FreeObject(ctx->request.InThisObjectId));
}
if (ctx->request.InThisObjectId == serviceGetObjectId(this->forward_service.get()) && !this->service_post_process_ctx->closed) {
/* If we're not longer MitMing anything, we'll no longer do any postprocessing. */
this->service_post_process_ctx->closed = true;
}
return ResultSuccess;
}
case DomainMessageType_SendMessage:
{
auto sub_obj = ctx->obj_holder->GetServiceObject<IDomainObject>()->GetObject(ctx->request.InThisObjectId);
if (sub_obj == nullptr) {
R_TRY(ForwardRequest(ctx));
return ResultSuccess;
}
dispatch_table = sub_obj->GetDispatchTable();
entry_count = sub_obj->GetDispatchTableEntryCount();
}
}
}
Result (*handler)(IpcResponseContext *ctx) = nullptr;
for (size_t i = 0; i < entry_count; i++) {
if (ctx->cmd_id == dispatch_table[i].cmd_id && dispatch_table[i].fw_low <= fw && fw <= dispatch_table[i].fw_high) {
handler = dispatch_table[i].handler;
break;
}
}
if (handler == nullptr) {
R_TRY(ForwardRequest(ctx));
return ResultSuccess;
}
R_TRY_CATCH(handler(ctx)) {
R_CATCH(ResultAtmosphereMitmShouldForwardToSession) {
std::memcpy(armGetTls(), this->backup_tls, sizeof(this->backup_tls));
R_TRY(ForwardRequest(ctx));
return ResultSuccess;
}
} R_END_TRY_CATCH;
return ResultSuccess;
}
virtual void PostProcessResponse(IpcResponseContext *ctx) override {
if ((ctx->cmd_type == IpcCommandType_Request || ctx->cmd_type == IpcCommandType_RequestWithContext) && R_SUCCEEDED(ctx->rc)) {
if (this->service_post_process_ctx->closed) {
return;
}
if (!IsDomainObject(ctx->obj_holder) || ctx->request.InThisObjectId == serviceGetObjectId(this->forward_service.get())) {
IMitmServiceObject *obj = nullptr;
if (!IsDomainObject(ctx->obj_holder)) {
obj = ctx->obj_holder->GetServiceObjectUnsafe<IMitmServiceObject>();
} else {
const auto sub_obj = ctx->obj_holder->GetServiceObject<IDomainObject>()->GetObject(ctx->request.InThisObjectId);
if (sub_obj != nullptr) {
obj = sub_obj->GetServiceObjectUnsafe<IMitmServiceObject>();
}
}
if (obj != nullptr) {
this->service_post_process_ctx->handler(obj, ctx);
}
}
}
}
virtual void CleanupResponse(IpcResponseContext *ctx) override {
/* Cleanup tls backup. */
std::memset(this->backup_tls, 0, sizeof(this->backup_tls));
/* Clean up copy handles. */
for (unsigned int i = 0; i < ctx->request.NumHandles; i++) {
if (ctx->request.WasHandleCopied[i]) {
svcCloseHandle(ctx->request.Handles[i]);
}
}
for (unsigned int i = 0; i < this->num_fwd_copy_hnds; i++) {
svcCloseHandle(this->fwd_copy_hnds[i]);
}
this->num_fwd_copy_hnds = 0;
}
public:
class IMitmHipcControlService : public IServiceObject {
private:
enum class CommandId {
ConvertCurrentObjectToDomain = 0,
CopyFromCurrentDomain = 1,
CloneCurrentObject = 2,
QueryPointerBufferSize = 3,
CloneCurrentObjectEx = 4,
};
private:
MitmSession *session;
public:
explicit IMitmHipcControlService(MitmSession *s) : session(s) {
}
virtual ~IMitmHipcControlService() override { }
public:
Result ConvertCurrentObjectToDomain(Out<u32> object_id) {
if (IsDomainObject(this->session->obj_holder)) {
return ResultKernelConnectionClosed;
}
R_TRY(serviceConvertToDomain(this->session->forward_service.get()));
u32 expected_id = serviceGetObjectId(this->session->forward_service.get());
/* Allocate new domain. */
auto new_domain = this->session->GetDomainManager()->AllocateDomain();
if (new_domain == nullptr) {
/* If our domains mismatch, we're in trouble. */
return ResultKernelConnectionClosed;
}
/* Reserve the expected object in the domain for our session. */
if (R_FAILED(new_domain->ReserveSpecificObject(expected_id))) {
return ResultKernelConnectionClosed;
}
new_domain->SetObject(expected_id, std::move(this->session->obj_holder));
this->session->obj_holder = std::move(ServiceObjectHolder(std::move(new_domain)));
/* Return the object id. */
object_id.SetValue(expected_id);
return ResultSuccess;
}
Result CopyFromCurrentDomain(Out<MovedHandle> out_h, u32 id) {
auto domain = this->session->obj_holder.GetServiceObject<IDomainObject>();
if (domain == nullptr) {
return ResultHipcTargetNotDomain;
}
auto object = domain->GetObject(id);
if (object == nullptr) {
/* Forward onwards. */
u32 *buf = (u32 *)armGetTls();
buf[0] = IpcCommandType_Control;
buf[1] = 0xA;
buf[4] = SFCI_MAGIC;
buf[5] = 0;
buf[6] = 1;
buf[7] = 0;
buf[8] = id;
buf[9] = 0;
R_TRY(ipcDispatch(this->session->forward_service->handle));
{
IpcParsedCommand r;
ipcParse(&r);
struct {
u64 magic;
u64 result;
} *raw = (decltype(raw))r.Raw;
R_TRY(raw->result);
out_h.SetValue(r.Handles[0]);
this->session->fwd_copy_hnds[this->session->num_fwd_copy_hnds++] = r.Handles[0];
}
return ResultSuccess;
}
Handle server_h, client_h;
R_ASSERT(SessionManagerBase::CreateSessionHandles(&server_h, &client_h));
out_h.SetValue(client_h);
if (id == serviceGetObjectId(this->session->forward_service.get())) {
this->session->GetSessionManager()->AddWaitable(new MitmSession(server_h, this->session->client_pid, this->session->forward_service, std::move(object->Clone()), this->session->service_post_process_ctx));
} else {
this->session->GetSessionManager()->AddSession(server_h, std::move(object->Clone()));
}
return ResultSuccess;
}
void CloneCurrentObject(Out<MovedHandle> out_h) {
Handle server_h, client_h;
R_ASSERT(SessionManagerBase::CreateSessionHandles(&server_h, &client_h));
this->session->GetSessionManager()->AddWaitable(new MitmSession(server_h, this->session->client_pid, this->session->forward_service, std::move(this->session->obj_holder.Clone()), this->session->service_post_process_ctx));
out_h.SetValue(client_h);
}
void QueryPointerBufferSize(Out<u16> size) {
size.SetValue(this->session->pointer_buffer.size());
}
void CloneCurrentObjectEx(Out<MovedHandle> out_h, u32 which) {
/* TODO: Figure out what this u32 controls. */
return CloneCurrentObject(out_h);
}
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(MitmSession::IMitmHipcControlService, ConvertCurrentObjectToDomain),
MAKE_SERVICE_COMMAND_META(MitmSession::IMitmHipcControlService, CopyFromCurrentDomain),
MAKE_SERVICE_COMMAND_META(MitmSession::IMitmHipcControlService, CloneCurrentObject),
MAKE_SERVICE_COMMAND_META(MitmSession::IMitmHipcControlService, QueryPointerBufferSize),
MAKE_SERVICE_COMMAND_META(MitmSession::IMitmHipcControlService, CloneCurrentObjectEx),
};
};
};

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "ncm/ncm_types.hpp"

View File

@@ -0,0 +1,432 @@
/*
* Copyright (c) 2018-2019 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 <type_traits>
namespace sts::ncm {
/* Storage IDs. */
enum class StorageId : u8 {
None = 0,
Host = 1,
GameCard = 2,
NandSystem = 3,
NandUser = 4,
SdCard = 5,
};
/* Title IDs. */
struct TitleId {
u64 value;
inline explicit operator u64() const {
return this->value;
}
/* Invalid Title ID. */
static const TitleId Invalid;
/* System Modules. */
static const TitleId SystemStart;
static const TitleId Fs;
static const TitleId Loader;
static const TitleId Ncm;
static const TitleId Pm;
static const TitleId Sm;
static const TitleId Boot;
static const TitleId Usb;
static const TitleId Tma;
static const TitleId Boot2;
static const TitleId Settings;
static const TitleId Bus;
static const TitleId Bluetooth;
static const TitleId Bcat;
static const TitleId Dmnt;
static const TitleId Friends;
static const TitleId Nifm;
static const TitleId Ptm;
static const TitleId Shell;
static const TitleId BsdSockets;
static const TitleId Hid;
static const TitleId Audio;
static const TitleId LogManager;
static const TitleId Wlan;
static const TitleId Cs;
static const TitleId Ldn;
static const TitleId NvServices;
static const TitleId Pcv;
static const TitleId Ppc;
static const TitleId NvnFlinger;
static const TitleId Pcie;
static const TitleId Account;
static const TitleId Ns;
static const TitleId Nfc;
static const TitleId Psc;
static const TitleId CapSrv;
static const TitleId Am;
static const TitleId Ssl;
static const TitleId Nim;
static const TitleId Cec;
static const TitleId Tspm;
static const TitleId Spl;
static const TitleId Lbl;
static const TitleId Btm;
static const TitleId Erpt;
static const TitleId Time;
static const TitleId Vi;
static const TitleId Pctl;
static const TitleId Npns;
static const TitleId Eupld;
static const TitleId Arp;
static const TitleId Glue;
static const TitleId Eclct;
static const TitleId Es;
static const TitleId Fatal;
static const TitleId Grc;
static const TitleId Creport;
static const TitleId Ro;
static const TitleId Profiler;
static const TitleId Sdb;
static const TitleId Migration;
static const TitleId Jit;
static const TitleId JpegDec;
static const TitleId SafeMode;
static const TitleId Olsc;
static const TitleId Dt;
static const TitleId Nd;
static const TitleId SystemEnd;
/* System Data Archives. */
static const TitleId ArchiveStart;
static const TitleId ArchiveCertStore;
static const TitleId ArchiveErrorMessage;
static const TitleId ArchiveMiiModel;
static const TitleId ArchiveBrowserDll;
static const TitleId ArchiveHelp;
static const TitleId ArchiveSharedFont;
static const TitleId ArchiveNgWord;
static const TitleId ArchiveSsidList;
static const TitleId ArchiveDictionary;
static const TitleId ArchiveSystemVersion;
static const TitleId ArchiveAvatarImage;
static const TitleId ArchiveLocalNews;
static const TitleId ArchiveEula;
static const TitleId ArchiveUrlBlackList;
static const TitleId ArchiveTimeZoneBinar;
static const TitleId ArchiveCertStoreCruiser;
static const TitleId ArchiveFontNintendoExtension;
static const TitleId ArchiveFontStandard;
static const TitleId ArchiveFontKorean;
static const TitleId ArchiveFontChineseTraditional;
static const TitleId ArchiveFontChineseSimple;
static const TitleId ArchiveFontBfcpx;
static const TitleId ArchiveSystemUpdate;
static const TitleId ArchiveFirmwareDebugSettings;
static const TitleId ArchiveBootImagePackage;
static const TitleId ArchiveBootImagePackageSafe;
static const TitleId ArchiveBootImagePackageExFat;
static const TitleId ArchiveBootImagePackageExFatSafe;
static const TitleId ArchiveFatalMessage;
static const TitleId ArchiveControllerIcon;
static const TitleId ArchivePlatformConfigIcosa;
static const TitleId ArchivePlatformConfigCopper;
static const TitleId ArchivePlatformConfigHoag;
static const TitleId ArchiveControllerFirmware;
static const TitleId ArchiveNgWord2;
static const TitleId ArchivePlatformConfigIcosaMariko;
static const TitleId ArchiveApplicationBlackList;
static const TitleId ArchiveRebootlessSystemUpdateVersion;
static const TitleId ArchiveContentActionTable;
static const TitleId ArchiveEnd;
/* System Applets. */
static const TitleId AppletStart;
static const TitleId AppletQlaunch;
static const TitleId AppletAuth;
static const TitleId AppletCabinet;
static const TitleId AppletController;
static const TitleId AppletDataErase;
static const TitleId AppletError;
static const TitleId AppletNetConnect;
static const TitleId AppletPlayerSelect;
static const TitleId AppletSwkbd;
static const TitleId AppletMiiEdit;
static const TitleId AppletWeb;
static const TitleId AppletShop;
static const TitleId AppletOverlayDisp;
static const TitleId AppletPhotoViewer;
static const TitleId AppletSet;
static const TitleId AppletOfflineWeb;
static const TitleId AppletLoginShare;
static const TitleId AppletWifiWebAuth;
static const TitleId AppletStarter;
static const TitleId AppletMyPage;
static const TitleId AppletPlayReport;
static const TitleId AppletMaintenanceMenu;
static const TitleId AppletGift;
static const TitleId AppletDummyShop;
static const TitleId AppletUserMigration;
static const TitleId AppletEncounter;
static const TitleId AppletStory;
static const TitleId AppletEnd;
/* Debug Applets. */
/* Debug Modules. */
/* Factory Setup. */
/* Applications. */
static const TitleId ApplicationStart;
static const TitleId ApplicationEnd;
/* Atmosphere Extensions. */
static const TitleId AtmosphereMitm;
};
/* Invalid Title ID. */
inline constexpr const TitleId TitleId::Invalid = {};
/* System Modules. */
inline constexpr const TitleId TitleId::SystemStart = { 0x0100000000000000ul };
inline constexpr const TitleId TitleId::Fs = { 0x0100000000000000ul };
inline constexpr const TitleId TitleId::Loader = { 0x0100000000000001ul };
inline constexpr const TitleId TitleId::Ncm = { 0x0100000000000002ul };
inline constexpr const TitleId TitleId::Pm = { 0x0100000000000003ul };
inline constexpr const TitleId TitleId::Sm = { 0x0100000000000004ul };
inline constexpr const TitleId TitleId::Boot = { 0x0100000000000005ul };
inline constexpr const TitleId TitleId::Usb = { 0x0100000000000006ul };
inline constexpr const TitleId TitleId::Tma = { 0x0100000000000007ul };
inline constexpr const TitleId TitleId::Boot2 = { 0x0100000000000008ul };
inline constexpr const TitleId TitleId::Settings = { 0x0100000000000009ul };
inline constexpr const TitleId TitleId::Bus = { 0x010000000000000Aul };
inline constexpr const TitleId TitleId::Bluetooth = { 0x010000000000000Bul };
inline constexpr const TitleId TitleId::Bcat = { 0x010000000000000Cul };
inline constexpr const TitleId TitleId::Dmnt = { 0x010000000000000Dul };
inline constexpr const TitleId TitleId::Friends = { 0x010000000000000Eul };
inline constexpr const TitleId TitleId::Nifm = { 0x010000000000000Ful };
inline constexpr const TitleId TitleId::Ptm = { 0x0100000000000010ul };
inline constexpr const TitleId TitleId::Shell = { 0x0100000000000011ul };
inline constexpr const TitleId TitleId::BsdSockets = { 0x0100000000000012ul };
inline constexpr const TitleId TitleId::Hid = { 0x0100000000000013ul };
inline constexpr const TitleId TitleId::Audio = { 0x0100000000000014ul };
inline constexpr const TitleId TitleId::LogManager = { 0x0100000000000015ul };
inline constexpr const TitleId TitleId::Wlan = { 0x0100000000000016ul };
inline constexpr const TitleId TitleId::Cs = { 0x0100000000000017ul };
inline constexpr const TitleId TitleId::Ldn = { 0x0100000000000018ul };
inline constexpr const TitleId TitleId::NvServices = { 0x0100000000000019ul };
inline constexpr const TitleId TitleId::Pcv = { 0x010000000000001Aul };
inline constexpr const TitleId TitleId::Ppc = { 0x010000000000001Bul };
inline constexpr const TitleId TitleId::NvnFlinger = { 0x010000000000001Cul };
inline constexpr const TitleId TitleId::Pcie = { 0x010000000000001Dul };
inline constexpr const TitleId TitleId::Account = { 0x010000000000001Eul };
inline constexpr const TitleId TitleId::Ns = { 0x010000000000001Ful };
inline constexpr const TitleId TitleId::Nfc = { 0x0100000000000020ul };
inline constexpr const TitleId TitleId::Psc = { 0x0100000000000021ul };
inline constexpr const TitleId TitleId::CapSrv = { 0x0100000000000022ul };
inline constexpr const TitleId TitleId::Am = { 0x0100000000000023ul };
inline constexpr const TitleId TitleId::Ssl = { 0x0100000000000024ul };
inline constexpr const TitleId TitleId::Nim = { 0x0100000000000025ul };
inline constexpr const TitleId TitleId::Cec = { 0x0100000000000026ul };
inline constexpr const TitleId TitleId::Tspm = { 0x0100000000000027ul };
inline constexpr const TitleId TitleId::Spl = { 0x0100000000000028ul };
inline constexpr const TitleId TitleId::Lbl = { 0x0100000000000029ul };
inline constexpr const TitleId TitleId::Btm = { 0x010000000000002Aul };
inline constexpr const TitleId TitleId::Erpt = { 0x010000000000002Bul };
inline constexpr const TitleId TitleId::Time = { 0x010000000000002Cul };
inline constexpr const TitleId TitleId::Vi = { 0x010000000000002Dul };
inline constexpr const TitleId TitleId::Pctl = { 0x010000000000002Eul };
inline constexpr const TitleId TitleId::Npns = { 0x010000000000002Ful };
inline constexpr const TitleId TitleId::Eupld = { 0x0100000000000030ul };
inline constexpr const TitleId TitleId::Arp = { 0x0100000000000031ul };
inline constexpr const TitleId TitleId::Glue = { 0x0100000000000031ul };
inline constexpr const TitleId TitleId::Eclct = { 0x0100000000000032ul };
inline constexpr const TitleId TitleId::Es = { 0x0100000000000033ul };
inline constexpr const TitleId TitleId::Fatal = { 0x0100000000000034ul };
inline constexpr const TitleId TitleId::Grc = { 0x0100000000000035ul };
inline constexpr const TitleId TitleId::Creport = { 0x0100000000000036ul };
inline constexpr const TitleId TitleId::Ro = { 0x0100000000000037ul };
inline constexpr const TitleId TitleId::Profiler = { 0x0100000000000038ul };
inline constexpr const TitleId TitleId::Sdb = { 0x0100000000000039ul };
inline constexpr const TitleId TitleId::Migration = { 0x010000000000003Aul };
inline constexpr const TitleId TitleId::Jit = { 0x010000000000003Bul };
inline constexpr const TitleId TitleId::JpegDec = { 0x010000000000003Cul };
inline constexpr const TitleId TitleId::SafeMode = { 0x010000000000003Dul };
inline constexpr const TitleId TitleId::Olsc = { 0x010000000000003Eul };
inline constexpr const TitleId TitleId::Dt = { 0x010000000000003Ful };
inline constexpr const TitleId TitleId::Nd = { 0x0100000000000040ul };
inline constexpr const TitleId TitleId::SystemEnd = { 0x01000000000007FFul };
/* System Data Archives. */
inline constexpr const TitleId TitleId::ArchiveStart = { 0x0100000000000800ul };
inline constexpr const TitleId TitleId::ArchiveCertStore = { 0x0100000000000800ul };
inline constexpr const TitleId TitleId::ArchiveErrorMessage = { 0x0100000000000801ul };
inline constexpr const TitleId TitleId::ArchiveMiiModel = { 0x0100000000000802ul };
inline constexpr const TitleId TitleId::ArchiveBrowserDll = { 0x0100000000000803ul };
inline constexpr const TitleId TitleId::ArchiveHelp = { 0x0100000000000804ul };
inline constexpr const TitleId TitleId::ArchiveSharedFont = { 0x0100000000000805ul };
inline constexpr const TitleId TitleId::ArchiveNgWord = { 0x0100000000000806ul };
inline constexpr const TitleId TitleId::ArchiveSsidList = { 0x0100000000000807ul };
inline constexpr const TitleId TitleId::ArchiveDictionary = { 0x0100000000000808ul };
inline constexpr const TitleId TitleId::ArchiveSystemVersion = { 0x0100000000000809ul };
inline constexpr const TitleId TitleId::ArchiveAvatarImage = { 0x010000000000080Aul };
inline constexpr const TitleId TitleId::ArchiveLocalNews = { 0x010000000000080Bul };
inline constexpr const TitleId TitleId::ArchiveEula = { 0x010000000000080Cul };
inline constexpr const TitleId TitleId::ArchiveUrlBlackList = { 0x010000000000080Dul };
inline constexpr const TitleId TitleId::ArchiveTimeZoneBinar = { 0x010000000000080Eul };
inline constexpr const TitleId TitleId::ArchiveCertStoreCruiser = { 0x010000000000080Ful };
inline constexpr const TitleId TitleId::ArchiveFontNintendoExtension = { 0x0100000000000810ul };
inline constexpr const TitleId TitleId::ArchiveFontStandard = { 0x0100000000000811ul };
inline constexpr const TitleId TitleId::ArchiveFontKorean = { 0x0100000000000812ul };
inline constexpr const TitleId TitleId::ArchiveFontChineseTraditional = { 0x0100000000000813ul };
inline constexpr const TitleId TitleId::ArchiveFontChineseSimple = { 0x0100000000000814ul };
inline constexpr const TitleId TitleId::ArchiveFontBfcpx = { 0x0100000000000815ul };
inline constexpr const TitleId TitleId::ArchiveSystemUpdate = { 0x0100000000000816ul };
inline constexpr const TitleId TitleId::ArchiveFirmwareDebugSettings = { 0x0100000000000818ul };
inline constexpr const TitleId TitleId::ArchiveBootImagePackage = { 0x0100000000000819ul };
inline constexpr const TitleId TitleId::ArchiveBootImagePackageSafe = { 0x010000000000081Aul };
inline constexpr const TitleId TitleId::ArchiveBootImagePackageExFat = { 0x010000000000081Bul };
inline constexpr const TitleId TitleId::ArchiveBootImagePackageExFatSafe = { 0x010000000000081Cul };
inline constexpr const TitleId TitleId::ArchiveFatalMessage = { 0x010000000000081Dul };
inline constexpr const TitleId TitleId::ArchiveControllerIcon = { 0x010000000000081Eul };
inline constexpr const TitleId TitleId::ArchivePlatformConfigIcosa = { 0x010000000000081Ful };
inline constexpr const TitleId TitleId::ArchivePlatformConfigCopper = { 0x0100000000000820ul };
inline constexpr const TitleId TitleId::ArchivePlatformConfigHoag = { 0x0100000000000821ul };
inline constexpr const TitleId TitleId::ArchiveControllerFirmware = { 0x0100000000000822ul };
inline constexpr const TitleId TitleId::ArchiveNgWord2 = { 0x0100000000000823ul };
inline constexpr const TitleId TitleId::ArchivePlatformConfigIcosaMariko = { 0x0100000000000824ul };
inline constexpr const TitleId TitleId::ArchiveApplicationBlackList = { 0x0100000000000825ul };
inline constexpr const TitleId TitleId::ArchiveRebootlessSystemUpdateVersion = { 0x0100000000000826ul };
inline constexpr const TitleId TitleId::ArchiveContentActionTable = { 0x0100000000000827ul };
inline constexpr const TitleId TitleId::ArchiveEnd = { 0x0100000000000FFFul };
/* System Applets. */
inline constexpr const TitleId TitleId::AppletStart = { 0x0100000000001000ul };
inline constexpr const TitleId TitleId::AppletQlaunch = { 0x0100000000001000ul };
inline constexpr const TitleId TitleId::AppletAuth = { 0x0100000000001001ul };
inline constexpr const TitleId TitleId::AppletCabinet = { 0x0100000000001002ul };
inline constexpr const TitleId TitleId::AppletController = { 0x0100000000001003ul };
inline constexpr const TitleId TitleId::AppletDataErase = { 0x0100000000001004ul };
inline constexpr const TitleId TitleId::AppletError = { 0x0100000000001005ul };
inline constexpr const TitleId TitleId::AppletNetConnect = { 0x0100000000001006ul };
inline constexpr const TitleId TitleId::AppletPlayerSelect = { 0x0100000000001007ul };
inline constexpr const TitleId TitleId::AppletSwkbd = { 0x0100000000001008ul };
inline constexpr const TitleId TitleId::AppletMiiEdit = { 0x0100000000001009ul };
inline constexpr const TitleId TitleId::AppletWeb = { 0x010000000000100Aul };
inline constexpr const TitleId TitleId::AppletShop = { 0x010000000000100Bul };
inline constexpr const TitleId TitleId::AppletOverlayDisp = { 0x010000000000100Cul };
inline constexpr const TitleId TitleId::AppletPhotoViewer = { 0x010000000000100Dul };
inline constexpr const TitleId TitleId::AppletSet = { 0x010000000000100Eul };
inline constexpr const TitleId TitleId::AppletOfflineWeb = { 0x010000000000100Ful };
inline constexpr const TitleId TitleId::AppletLoginShare = { 0x0100000000001010ul };
inline constexpr const TitleId TitleId::AppletWifiWebAuth = { 0x0100000000001011ul };
inline constexpr const TitleId TitleId::AppletStarter = { 0x0100000000001012ul };
inline constexpr const TitleId TitleId::AppletMyPage = { 0x0100000000001013ul };
inline constexpr const TitleId TitleId::AppletPlayReport = { 0x0100000000001014ul };
inline constexpr const TitleId TitleId::AppletMaintenanceMenu = { 0x0100000000001015ul };
inline constexpr const TitleId TitleId::AppletGift = { 0x010000000000101Aul };
inline constexpr const TitleId TitleId::AppletDummyShop = { 0x010000000000101Bul };
inline constexpr const TitleId TitleId::AppletUserMigration = { 0x010000000000101Cul };
inline constexpr const TitleId TitleId::AppletEncounter = { 0x010000000000101Dul };
inline constexpr const TitleId TitleId::AppletStory = { 0x0100000000001020ul };
inline constexpr const TitleId TitleId::AppletEnd = { 0x0100000000001FFFul };
/* Debug Applets. */
/* Debug Modules. */
/* Factory Setup. */
/* Applications. */
inline constexpr const TitleId TitleId::ApplicationStart = { 0x0100000000010000ul };
inline constexpr const TitleId TitleId::ApplicationEnd = { 0x01FFFFFFFFFFFFFFul };
/* Atmosphere Extensions. */
inline constexpr const TitleId TitleId::AtmosphereMitm = { 0x010041544D530000ul };
inline constexpr bool operator==(const TitleId &lhs, const TitleId &rhs) {
return lhs.value == rhs.value;
}
inline constexpr bool operator!=(const TitleId &lhs, const TitleId &rhs) {
return lhs.value != rhs.value;
}
inline constexpr bool operator<(const TitleId &lhs, const TitleId &rhs) {
return lhs.value < rhs.value;
}
inline constexpr bool operator<=(const TitleId &lhs, const TitleId &rhs) {
return lhs.value <= rhs.value;
}
inline constexpr bool operator>(const TitleId &lhs, const TitleId &rhs) {
return lhs.value > rhs.value;
}
inline constexpr bool operator>=(const TitleId &lhs, const TitleId &rhs) {
return lhs.value >= rhs.value;
}
inline constexpr bool IsSystemTitleId(const TitleId &title_id) {
return TitleId::SystemStart <= title_id && title_id <= TitleId::SystemEnd;
}
inline constexpr bool IsArchiveTitleId(const TitleId &title_id) {
return TitleId::ArchiveStart <= title_id && title_id <= TitleId::ArchiveEnd;
}
inline constexpr bool IsAppletTitleId(const TitleId &title_id) {
return TitleId::AppletStart <= title_id && title_id <= TitleId::AppletEnd;
}
inline constexpr bool IsApplicationTitleId(const TitleId &title_id) {
return TitleId::ApplicationStart <= title_id && title_id <= TitleId::ApplicationEnd;
}
static_assert(sizeof(TitleId) == sizeof(u64) && std::is_pod<TitleId>::value, "TitleId definition!");
/* Title Location. */
struct TitleLocation {
TitleId title_id;
u8 storage_id;
static constexpr TitleLocation Make(TitleId title_id, StorageId storage_id) {
return { .title_id = title_id, .storage_id = static_cast<u8>(storage_id), };
}
};
static_assert(sizeof(TitleLocation) == 0x10 && std::is_pod<TitleLocation>::value, "TitleLocation definition!");
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include <cstdlib>
#include "services/bpc_ams.h"
static constexpr size_t AtmosphereFatalErrorNumGprs = 29;
static constexpr u32 AtmosphereFatalErrorMagic = 0x31454641; /* "AFE1" */
/* Will be called by libstratosphere on crash. */
void StratosphereCrashHandler(ThreadExceptionDump *ctx);

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "patcher/patcher_api.hpp"

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2019 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 "../ro/ro_types.hpp"
namespace sts::patcher {
/* Helper for applying to code binaries. */
void LocateAndApplyIpsPatchesToModule(const char *patch_dir, size_t protected_size, size_t offset, const ro::ModuleId *module_id, u8 *mapped_module, size_t mapped_size);
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "pm/pm_types.hpp"
#include "pm/pm_boot_mode_api.hpp"
#include "pm/pm_info_api.hpp"
#include "pm/pm_shell_api.hpp"

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2018-2019 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 "pm_types.hpp"
namespace sts::pm::bm {
/* Boot Mode API. */
BootMode GetBootMode();
void SetMaintenanceBoot();
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2018-2019 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 "pm_types.hpp"
#include "../ncm/ncm_types.hpp"
namespace sts::pm::info {
/* Information API. */
Result GetTitleId(ncm::TitleId *out_title_id, u64 process_id);
Result GetProcessId(u64 *out_process_id, ncm::TitleId title_id);
Result HasLaunchedTitle(bool *out, ncm::TitleId title_id);
/* Information convenience API. */
bool HasLaunchedTitle(ncm::TitleId title_id);
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2018-2019 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 "../ldr.hpp"
#include "pm_types.hpp"
namespace sts::pm::shell {
/* Shell API. */
Result LaunchTitle(u64 *out_process_id, const ncm::TitleLocation &loc, u32 launch_flags);
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
namespace sts::pm {
enum class BootMode {
Normal = 0,
Maintenance = 1,
SafeMode = 2,
};
enum ResourceLimitGroup {
ResourceLimitGroup_System = 0,
ResourceLimitGroup_Application = 1,
ResourceLimitGroup_Applet = 2,
ResourceLimitGroup_Count,
};
using LimitableResource = ::LimitableResource;
struct ProcessEventInfo {
u32 event;
u64 process_id;
};
static_assert(sizeof(ProcessEventInfo) == 0x10 && std::is_pod<ProcessEventInfo>::value, "ProcessEventInfo definition!");
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
namespace sts::reg {
inline void Write(volatile u32 *reg, u32 val) {
*reg = val;
}
inline void Write(uintptr_t reg, u32 val) {
Write(reinterpret_cast<volatile u32 *>(reg), val);
}
inline u32 Read(volatile u32 *reg) {
return *reg;
}
inline u32 Read(uintptr_t reg) {
return Read(reinterpret_cast<volatile u32 *>(reg));
}
inline void SetBits(volatile u32 *reg, u32 mask) {
*reg |= mask;
}
inline void SetBits(uintptr_t reg, u32 mask) {
SetBits(reinterpret_cast<volatile u32 *>(reg), mask);
}
inline void ClearBits(volatile u32 *reg, u32 mask) {
*reg &= ~mask;
}
inline void ClearBits(uintptr_t reg, u32 mask) {
ClearBits(reinterpret_cast<volatile u32 *>(reg), mask);
}
inline void MaskBits(volatile u32 *reg, u32 mask) {
*reg &= mask;
}
inline void MaskBits(uintptr_t reg, u32 mask) {
MaskBits(reinterpret_cast<volatile u32 *>(reg), mask);
}
inline void ReadWrite(volatile u32 *reg, u32 val, u32 mask) {
*reg = (*reg & (~mask)) | (val & mask);
}
inline void ReadWrite(uintptr_t reg, u32 val, u32 mask) {
ReadWrite(reinterpret_cast<volatile u32 *>(reg), val, mask);
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2018-2019 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
/* Utilities. */
#include "results/utilities.h"
/* Official. */
#include "results/creport_results.hpp"
#include "results/debug_results.hpp"
#include "results/dmnt_results.hpp"
#include "results/fatal_results.hpp"
#include "results/fs_results.hpp"
#include "results/hipc_results.hpp"
#include "results/i2c_results.hpp"
#include "results/kernel_results.hpp"
#include "results/kvdb_results.hpp"
#include "results/loader_results.hpp"
#include "results/lr_results.hpp"
#include "results/ncm_results.hpp"
#include "results/pm_results.hpp"
#include "results/ro_results.hpp"
#include "results/settings_results.hpp"
#include "results/sf_results.hpp"
#include "results/sm_results.hpp"
#include "results/spl_results.hpp"
#include "results/updater_results.hpp"
#include "results/vi_results.hpp"
/* Unofficial. */
#include "results/ams_results.hpp"
static constexpr Result ResultSuccess = 0;

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
/* Please note: These results are all custom, and not official. */
static constexpr u32 Module_Atmosphere = 444;
/* Result 1-1000 reserved for Atmosphere. */
static constexpr Result ResultAtmosphereExosphereNotPresent = MAKERESULT(Module_Atmosphere, 1);
static constexpr Result ResultAtmosphereVersionMismatch = MAKERESULT(Module_Atmosphere, 2);
/* Results 1000-2000 reserved for Atmosphere Mitm. */
static constexpr Result ResultAtmosphereMitmShouldForwardToSession = MAKERESULT(Module_Atmosphere, 1000);
static constexpr Result ResultAtmosphereMitmProcessNotAssociated = MAKERESULT(Module_Atmosphere, 1100);

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
static constexpr u32 Module_Creport = 168;
static constexpr Result ResultCreportUndefinedInstruction = MAKERESULT(Module_Creport, 0);
static constexpr Result ResultCreportInstructionAbort = MAKERESULT(Module_Creport, 1);
static constexpr Result ResultCreportDataAbort = MAKERESULT(Module_Creport, 2);
static constexpr Result ResultCreportAlignmentFault = MAKERESULT(Module_Creport, 3);
static constexpr Result ResultCreportDebuggerAttached = MAKERESULT(Module_Creport, 4);
static constexpr Result ResultCreportBreakPoint = MAKERESULT(Module_Creport, 5);
static constexpr Result ResultCreportUserBreak = MAKERESULT(Module_Creport, 6);
static constexpr Result ResultCreportDebuggerBreak = MAKERESULT(Module_Creport, 7);
static constexpr Result ResultCreportUndefinedSystemCall = MAKERESULT(Module_Creport, 8);
static constexpr Result ResultCreportSystemMemoryError = MAKERESULT(Module_Creport, 9);
static constexpr Result ResultCreportIncompleteReport = MAKERESULT(Module_Creport, 99);

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
static constexpr u32 Module_Debug = 183;
static constexpr Result ResultDebugCannotDebug = MAKERESULT(Module_Debug, 1);
static constexpr Result ResultDebugAlreadyAttached = MAKERESULT(Module_Debug, 2);
static constexpr Result ResultDebugCancelled = MAKERESULT(Module_Debug, 3);

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
static constexpr u32 Module_Dmnt = 13;
static constexpr Result ResultDmntUnknown = MAKERESULT(Module_Dmnt, 1);
static constexpr Result ResultDmntDebuggingDisabled = MAKERESULT(Module_Dmnt, 2);
static constexpr Result ResultDmntCheatNotAttached = MAKERESULT(Module_Dmnt, 6500);
static constexpr Result ResultDmntCheatNullBuffer = MAKERESULT(Module_Dmnt, 6501);
static constexpr Result ResultDmntCheatInvalidBuffer = MAKERESULT(Module_Dmnt, 6502);
static constexpr Result ResultDmntCheatUnknownChtId = MAKERESULT(Module_Dmnt, 6503);
static constexpr Result ResultDmntCheatOutOfCheats = MAKERESULT(Module_Dmnt, 6504);
static constexpr Result ResultDmntCheatInvalidCheat = MAKERESULT(Module_Dmnt, 6505);
static constexpr Result ResultDmntCheatCannotDisableMasterCheat = MAKERESULT(Module_Dmnt, 6505);
static constexpr Result ResultDmntCheatInvalidFreezeWidth = MAKERESULT(Module_Dmnt, 6600);
static constexpr Result ResultDmntCheatAddressAlreadyFrozen = MAKERESULT(Module_Dmnt, 6601);
static constexpr Result ResultDmntCheatAddressNotFrozen = MAKERESULT(Module_Dmnt, 6602);
static constexpr Result ResultDmntCheatTooManyFrozenAddresses = MAKERESULT(Module_Dmnt, 6603);
static constexpr Result ResultDmntCheatVmInvalidCondDepth = MAKERESULT(Module_Dmnt, 6700);

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
static constexpr u32 Module_Fatal = 163;
static constexpr Result ResultFatalAllocationFailed = MAKERESULT(Module_Fatal, 1);
static constexpr Result ResultFatalNullGraphicsBuffer = MAKERESULT(Module_Fatal, 2);
static constexpr Result ResultFatalAlreadyThrown = MAKERESULT(Module_Fatal, 3);
static constexpr Result ResultFatalTooManyEvents = MAKERESULT(Module_Fatal, 4);
static constexpr Result ResultFatalInRepairWithoutVolHeld = MAKERESULT(Module_Fatal, 5);
static constexpr Result ResultFatalInRepairWithoutTimeReviserCartridge = MAKERESULT(Module_Fatal, 6);

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
static constexpr u32 Module_Fs = 2;
static constexpr Result ResultFsPathNotFound = MAKERESULT(Module_Fs, 1);
static constexpr Result ResultFsPathAlreadyExists = MAKERESULT(Module_Fs, 2);
static constexpr Result ResultFsTargetLocked = MAKERESULT(Module_Fs, 7);
static constexpr Result ResultFsDirectoryNotEmpty = MAKERESULT(Module_Fs, 8);
static constexpr Result ResultFsNotEnoughFreeSpaceRangeStart = MAKERESULT(Module_Fs, 30);
static constexpr Result ResultFsNotEnoughFreeSpaceBisRangeStart = MAKERESULT(Module_Fs, 34);
static constexpr Result ResultFsNotEnoughFreeSpaceBisCalibration = MAKERESULT(Module_Fs, 35);
static constexpr Result ResultFsNotEnoughFreeSpaceBisSafe = MAKERESULT(Module_Fs, 36);
static constexpr Result ResultFsNotEnoughFreeSpaceBisUser = MAKERESULT(Module_Fs, 37);
static constexpr Result ResultFsNotEnoughFreeSpaceBisSystem = MAKERESULT(Module_Fs, 38);
static constexpr Result ResultFsNotEnoughFreeSpaceBisRangeEnd = MAKERESULT(Module_Fs, 39);
static constexpr Result ResultFsNotEnoughFreeSpaceSdCard = MAKERESULT(Module_Fs, 39);
static constexpr Result ResultFsNotEnoughFreeSpaceRangeEnd = MAKERESULT(Module_Fs, 45);
static constexpr Result ResultFsMountNameAlreadyExists = MAKERESULT(Module_Fs, 60);
static constexpr Result ResultFsTargetNotFound = MAKERESULT(Module_Fs, 1002);
static constexpr Result ResultFsSdCardNotPresent = MAKERESULT(Module_Fs, 2001);
static constexpr Result ResultFsNotImplemented = MAKERESULT(Module_Fs, 3001);
static constexpr Result ResultFsOutOfRange = MAKERESULT(Module_Fs, 3005);
static constexpr Result ResultFsAllocationFailureInDirectorySaveDataFileSystem = MAKERESULT(Module_Fs, 3321);
static constexpr Result ResultFsAllocationFailureInSubDirectoryFileSystem = MAKERESULT(Module_Fs, 3355);
static constexpr Result ResultFsPreconditionViolation = MAKERESULT(Module_Fs, 6000);
static constexpr Result ResultFsInvalidArgument = MAKERESULT(Module_Fs, 6001);
static constexpr Result ResultFsInvalidPath = MAKERESULT(Module_Fs, 6002);
static constexpr Result ResultFsTooLongPath = MAKERESULT(Module_Fs, 6003);
static constexpr Result ResultFsInvalidCharacter = MAKERESULT(Module_Fs, 6004);
static constexpr Result ResultFsInvalidPathFormat = MAKERESULT(Module_Fs, 6005);
static constexpr Result ResultFsDirectoryUnobtainable = MAKERESULT(Module_Fs, 6006);
static constexpr Result ResultFsNotNormalized = MAKERESULT(Module_Fs, 6007);
static constexpr Result ResultFsInvalidOffset = MAKERESULT(Module_Fs, 6061);
static constexpr Result ResultFsInvalidSize = MAKERESULT(Module_Fs, 6062);
static constexpr Result ResultFsNullptrArgument = MAKERESULT(Module_Fs, 6063);
static constexpr Result ResultFsInvalidSaveDataSpaceId = MAKERESULT(Module_Fs, 6082);
static constexpr Result ResultFsUnsupportedOperation = MAKERESULT(Module_Fs, 6300);
static constexpr Result ResultFsPermissionDenied = MAKERESULT(Module_Fs, 6400);

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
static constexpr u32 Module_Hipc = 11;
static constexpr Result ResultHipcOutOfDomains = MAKERESULT(Module_Hipc, 200);
static constexpr Result ResultHipcSessionClosed = MAKERESULT(Module_Hipc, 301);
static constexpr Result ResultHipcTargetNotDomain = MAKERESULT(Module_Hipc, 491);
static constexpr Result ResultHipcDomainObjectNotFound = MAKERESULT(Module_Hipc, 492);

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
static constexpr u32 Module_I2c = 101;
static constexpr Result ResultI2cNoAck = MAKERESULT(Module_I2c, 1);
static constexpr Result ResultI2cBusBusy = MAKERESULT(Module_I2c, 2);
static constexpr Result ResultI2cFullCommandList = MAKERESULT(Module_I2c, 3);
static constexpr Result ResultI2cTimedOut = MAKERESULT(Module_I2c, 4);
static constexpr Result ResultI2cUnknownDevice = MAKERESULT(Module_I2c, 5);

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
/* libnx already has: static constexpr u32 Module_Kernel = 1; */
static constexpr Result ResultKernelOutOfSessions = MAKERESULT(Module_Kernel, KernelError_OutOfSessions);
static constexpr Result ResultKernelInvalidCapabilityDescriptor = MAKERESULT(Module_Kernel, KernelError_InvalidCapabilityDescriptor);
static constexpr Result ResultKernelNotImplemented = MAKERESULT(Module_Kernel, KernelError_NotImplemented);
static constexpr Result ResultKernelThreadTerminating = MAKERESULT(Module_Kernel, KernelError_ThreadTerminating);
static constexpr Result ResultKernelOutOfDebugEvents = MAKERESULT(Module_Kernel, KernelError_OutOfDebugEvents);
static constexpr Result ResultKernelInvalidSize = MAKERESULT(Module_Kernel, KernelError_InvalidSize);
static constexpr Result ResultKernelInvalidAddress = MAKERESULT(Module_Kernel, KernelError_InvalidAddress);
static constexpr Result ResultKernelResourceExhausted = MAKERESULT(Module_Kernel, KernelError_ResourceExhausted);
static constexpr Result ResultKernelOutOfMemory = MAKERESULT(Module_Kernel, KernelError_OutOfMemory);
static constexpr Result ResultKernelOutOfHandles = MAKERESULT(Module_Kernel, KernelError_OutOfHandles);
static constexpr Result ResultKernelInvalidMemoryState = MAKERESULT(Module_Kernel, KernelError_InvalidMemoryState);
static constexpr Result ResultKernelInvalidMemoryPermissions = MAKERESULT(Module_Kernel, KernelError_InvalidMemoryPermissions);
static constexpr Result ResultKernelInvalidMemoryRange = MAKERESULT(Module_Kernel, KernelError_InvalidMemoryRange);
static constexpr Result ResultKernelInvalidPriority = MAKERESULT(Module_Kernel, KernelError_InvalidPriority);
static constexpr Result ResultKernelInvalidCoreId = MAKERESULT(Module_Kernel, KernelError_InvalidCoreId);
static constexpr Result ResultKernelInvalidHandle = MAKERESULT(Module_Kernel, KernelError_InvalidHandle);
static constexpr Result ResultKernelInvalidUserBuffer = MAKERESULT(Module_Kernel, KernelError_InvalidUserBuffer);
static constexpr Result ResultKernelInvalidCombination = MAKERESULT(Module_Kernel, KernelError_InvalidCombination);
static constexpr Result ResultKernelTimedOut = MAKERESULT(Module_Kernel, KernelError_TimedOut);
static constexpr Result ResultKernelCancelled = MAKERESULT(Module_Kernel, KernelError_Cancelled);
static constexpr Result ResultKernelOutOfRange = MAKERESULT(Module_Kernel, KernelError_OutOfRange);
static constexpr Result ResultKernelInvalidEnumValue = MAKERESULT(Module_Kernel, KernelError_InvalidEnumValue);
static constexpr Result ResultKernelNotFound = MAKERESULT(Module_Kernel, KernelError_NotFound);
static constexpr Result ResultKernelAlreadyExists = MAKERESULT(Module_Kernel, KernelError_AlreadyExists);
static constexpr Result ResultKernelConnectionClosed = MAKERESULT(Module_Kernel, KernelError_ConnectionClosed);
static constexpr Result ResultKernelUnhandledUserInterrupt = MAKERESULT(Module_Kernel, KernelError_UnhandledUserInterrupt);
static constexpr Result ResultKernelInvalidState = MAKERESULT(Module_Kernel, KernelError_InvalidState);
static constexpr Result ResultKernelReservedValue = MAKERESULT(Module_Kernel, KernelError_ReservedValue);
static constexpr Result ResultKernelInvalidHwBreakpoint = MAKERESULT(Module_Kernel, KernelError_InvalidHwBreakpoint);
static constexpr Result ResultKernelFatalUserException = MAKERESULT(Module_Kernel, KernelError_FatalUserException);
static constexpr Result ResultKernelOwnedByAnotherProcess = MAKERESULT(Module_Kernel, KernelError_OwnedByAnotherProcess);
static constexpr Result ResultKernelConnectionRefused = MAKERESULT(Module_Kernel, KernelError_ConnectionRefused);
static constexpr Result ResultKernelLimitReached = MAKERESULT(Module_Kernel, 132 /* KernelError_OutOfResource */);
static constexpr Result ResultKernelIpcMapFailed = MAKERESULT(Module_Kernel, KernelError_IpcMapFailed);
static constexpr Result ResultKernelIpcCmdBufTooSmall = MAKERESULT(Module_Kernel, KernelError_IpcCmdbufTooSmall);
static constexpr Result ResultKernelNotDebugged = MAKERESULT(Module_Kernel, KernelError_NotDebugged);

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
static constexpr u32 Module_Kvdb = 20;
static constexpr Result ResultKvdbKeyCapacityInsufficient = MAKERESULT(Module_Kvdb, 1);
static constexpr Result ResultKvdbKeyNotFound = MAKERESULT(Module_Kvdb, 2);
static constexpr Result ResultKvdbAllocationFailed = MAKERESULT(Module_Kvdb, 4);
static constexpr Result ResultKvdbInvalidKeyValue = MAKERESULT(Module_Kvdb, 5);
static constexpr Result ResultKvdbBufferInsufficient = MAKERESULT(Module_Kvdb, 6);
static constexpr Result ResultKvdbInvalidFilesystemState = MAKERESULT(Module_Kvdb, 8);
static constexpr Result ResultKvdbNotCreated = MAKERESULT(Module_Kvdb, 9);

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
static constexpr u32 Module_Loader = 9;
static constexpr Result ResultLoaderTooLongArgument = MAKERESULT(Module_Loader, 1);
static constexpr Result ResultLoaderTooManyArguments = MAKERESULT(Module_Loader, 2);
static constexpr Result ResultLoaderTooLargeMeta = MAKERESULT(Module_Loader, 3);
static constexpr Result ResultLoaderInvalidMeta = MAKERESULT(Module_Loader, 4);
static constexpr Result ResultLoaderInvalidNso = MAKERESULT(Module_Loader, 5);
static constexpr Result ResultLoaderInvalidPath = MAKERESULT(Module_Loader, 6);
static constexpr Result ResultLoaderTooManyProcesses = MAKERESULT(Module_Loader, 7);
static constexpr Result ResultLoaderNotPinned = MAKERESULT(Module_Loader, 8);
static constexpr Result ResultLoaderInvalidProgramId = MAKERESULT(Module_Loader, 9);
static constexpr Result ResultLoaderInvalidVersion = MAKERESULT(Module_Loader, 10);
static constexpr Result ResultLoaderInsufficientAddressSpace = MAKERESULT(Module_Loader, 51);
static constexpr Result ResultLoaderInvalidNro = MAKERESULT(Module_Loader, 52);
static constexpr Result ResultLoaderInvalidNrr = MAKERESULT(Module_Loader, 53);
static constexpr Result ResultLoaderInvalidSignature = MAKERESULT(Module_Loader, 54);
static constexpr Result ResultLoaderInsufficientNroRegistrations = MAKERESULT(Module_Loader, 55);
static constexpr Result ResultLoaderInsufficientNrrRegistrations = MAKERESULT(Module_Loader, 56);
static constexpr Result ResultLoaderNroAlreadyLoaded = MAKERESULT(Module_Loader, 57);
static constexpr Result ResultLoaderInvalidAddress = MAKERESULT(Module_Loader, 81);
static constexpr Result ResultLoaderInvalidSize = MAKERESULT(Module_Loader, 82);
static constexpr Result ResultLoaderNotLoaded = MAKERESULT(Module_Loader, 84);
static constexpr Result ResultLoaderNotRegistered = MAKERESULT(Module_Loader, 85);
static constexpr Result ResultLoaderInvalidSession = MAKERESULT(Module_Loader, 86);
static constexpr Result ResultLoaderInvalidProcess = MAKERESULT(Module_Loader, 87);
static constexpr Result ResultLoaderUnknownCapability = MAKERESULT(Module_Loader, 100);
static constexpr Result ResultLoaderInvalidCapabilityKernelFlags = MAKERESULT(Module_Loader, 103);
static constexpr Result ResultLoaderInvalidCapabilitySyscallMask = MAKERESULT(Module_Loader, 104);
static constexpr Result ResultLoaderInvalidCapabilityMapRange = MAKERESULT(Module_Loader, 106);
static constexpr Result ResultLoaderInvalidCapabilityMapPage = MAKERESULT(Module_Loader, 107);
static constexpr Result ResultLoaderInvalidCapabilityInterruptPair = MAKERESULT(Module_Loader, 111);
static constexpr Result ResultLoaderInvalidCapabilityApplicationType = MAKERESULT(Module_Loader, 113);
static constexpr Result ResultLoaderInvalidCapabilityKernelVersion = MAKERESULT(Module_Loader, 114);
static constexpr Result ResultLoaderInvalidCapabilityHandleTable = MAKERESULT(Module_Loader, 115);
static constexpr Result ResultLoaderInvalidCapabilityDebugFlags = MAKERESULT(Module_Loader, 116);
static constexpr Result ResultLoaderInternalError = MAKERESULT(Module_Loader, 200);

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
static constexpr u32 Module_Lr = 8;
static constexpr Result ResultLrProgramNotFound = MAKERESULT(Module_Lr, 2);
static constexpr Result ResultLrDataNotFound = MAKERESULT(Module_Lr, 3);
static constexpr Result ResultLrUnknownStorageId = MAKERESULT(Module_Lr, 4);
static constexpr Result ResultLrHtmlDocumentNotFound = MAKERESULT(Module_Lr, 6);
static constexpr Result ResultLrAddOnContentNotFound = MAKERESULT(Module_Lr, 7);
static constexpr Result ResultLrControlNotFound = MAKERESULT(Module_Lr, 8);
static constexpr Result ResultLrLegalInformationNotFound = MAKERESULT(Module_Lr, 9);
static constexpr Result ResultLrTooManyRegisteredPaths = MAKERESULT(Module_Lr, 90);

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
static constexpr u32 Module_Ncm = 5;
static constexpr Result ResultNcmPlaceHolderAlreadyExists = MAKERESULT(Module_Ncm, 2);
static constexpr Result ResultNcmPlaceHolderNotFound = MAKERESULT(Module_Ncm, 3);
static constexpr Result ResultNcmContentAlreadyExists = MAKERESULT(Module_Ncm, 4);
static constexpr Result ResultNcmContentNotFound = MAKERESULT(Module_Ncm, 5);
static constexpr Result ResultNcmContentMetaNotFound = MAKERESULT(Module_Ncm, 7);
static constexpr Result ResultNcmAllocationFailed = MAKERESULT(Module_Ncm, 8);
static constexpr Result ResultNcmUnknownStorage = MAKERESULT(Module_Ncm, 12);
static constexpr Result ResultNcmInvalidContentStorage = MAKERESULT(Module_Ncm, 100);
static constexpr Result ResultNcmInvalidContentMetaDatabase = MAKERESULT(Module_Ncm, 110);
static constexpr Result ResultNcmBufferInsufficient = MAKERESULT(Module_Ncm, 180);
static constexpr Result ResultNcmInvalidContentMetaKey = MAKERESULT(Module_Ncm, 240);
static constexpr Result ResultNcmContentStorageNotActive = MAKERESULT(Module_Ncm, 250);
static constexpr Result ResultNcmGameCardContentStorageNotActive = MAKERESULT(Module_Ncm, 251);
static constexpr Result ResultNcmNandSystemContentStorageNotActive = MAKERESULT(Module_Ncm, 252);
static constexpr Result ResultNcmNandUserContentStorageNotActive = MAKERESULT(Module_Ncm, 253);
static constexpr Result ResultNcmSdCardContentStorageNotActive = MAKERESULT(Module_Ncm, 254);
static constexpr Result ResultNcmUnknownContentStorageNotActive = MAKERESULT(Module_Ncm, 258);
static constexpr Result ResultNcmContentMetaDatabaseNotActive = MAKERESULT(Module_Ncm, 260);
static constexpr Result ResultNcmGameCardContentMetaDatabaseNotActive = MAKERESULT(Module_Ncm, 261);
static constexpr Result ResultNcmNandSystemContentMetaDatabaseNotActive = MAKERESULT(Module_Ncm, 262);
static constexpr Result ResultNcmNandUserContentMetaDatabaseNotActive = MAKERESULT(Module_Ncm, 263);
static constexpr Result ResultNcmSdCardContentMetaDatabaseNotActive = MAKERESULT(Module_Ncm, 264);
static constexpr Result ResultNcmUnknownContentMetaDatabaseNotActive = MAKERESULT(Module_Ncm, 268);
static constexpr Result ResultNcmInvalidArgument = MAKERESULT(Module_Ncm, 8181);
static constexpr Result ResultNcmInvalidOffset = MAKERESULT(Module_Ncm, 8182);

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
static constexpr u32 Module_Pm = 15;
static constexpr Result ResultPmProcessNotFound = MAKERESULT(Module_Pm, 1);
static constexpr Result ResultPmAlreadyStarted = MAKERESULT(Module_Pm, 2);
static constexpr Result ResultPmNotExited = MAKERESULT(Module_Pm, 3);
static constexpr Result ResultPmDebugHookInUse = MAKERESULT(Module_Pm, 4);
static constexpr Result ResultPmApplicationRunning = MAKERESULT(Module_Pm, 5);
static constexpr Result ResultPmInvalidSize = MAKERESULT(Module_Pm, 6);

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
static constexpr u32 Module_Ro = 22;
static constexpr Result ResultRoInsufficientAddressSpace = MAKERESULT(Module_Ro, 2);
static constexpr Result ResultRoAlreadyLoaded = MAKERESULT(Module_Ro, 3);
static constexpr Result ResultRoInvalidNro = MAKERESULT(Module_Ro, 4);
static constexpr Result ResultRoInvalidNrr = MAKERESULT(Module_Ro, 6);
static constexpr Result ResultRoTooManyNro = MAKERESULT(Module_Ro, 7);
static constexpr Result ResultRoTooManyNrr = MAKERESULT(Module_Ro, 8);
static constexpr Result ResultRoNotAuthorized = MAKERESULT(Module_Ro, 9);
static constexpr Result ResultRoInvalidNrrType = MAKERESULT(Module_Ro, 10);
static constexpr Result ResultRoInternalError = MAKERESULT(Module_Ro, 1023);
static constexpr Result ResultRoInvalidAddress = MAKERESULT(Module_Ro, 1025);
static constexpr Result ResultRoInvalidSize = MAKERESULT(Module_Ro, 1026);
static constexpr Result ResultRoNotLoaded = MAKERESULT(Module_Ro, 1028);
static constexpr Result ResultRoNotRegistered = MAKERESULT(Module_Ro, 1029);
static constexpr Result ResultRoInvalidSession = MAKERESULT(Module_Ro, 1030);
static constexpr Result ResultRoInvalidProcess = MAKERESULT(Module_Ro, 1031);

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
static constexpr u32 Module_Settings = 105;
static constexpr Result ResultSettingsItemNotFound = MAKERESULT(Module_Settings, 11);
static constexpr Result ResultSettingsItemKeyAllocationFailed = MAKERESULT(Module_Settings, 101);
static constexpr Result ResultSettingsItemValueAllocationFailed = MAKERESULT(Module_Settings, 102);
static constexpr Result ResultSettingsItemNameNull = MAKERESULT(Module_Settings, 201);
static constexpr Result ResultSettingsItemKeyNull = MAKERESULT(Module_Settings, 202);
static constexpr Result ResultSettingsItemValueNull = MAKERESULT(Module_Settings, 203);
static constexpr Result ResultSettingsItemKeyBufferNull = MAKERESULT(Module_Settings, 204);
static constexpr Result ResultSettingsItemValueBufferNull = MAKERESULT(Module_Settings, 205);
static constexpr Result ResultSettingsItemNameEmpty = MAKERESULT(Module_Settings, 221);
static constexpr Result ResultSettingsItemKeyEmpty = MAKERESULT(Module_Settings, 222);
static constexpr Result ResultSettingsItemNameTooLong = MAKERESULT(Module_Settings, 241);
static constexpr Result ResultSettingsItemKeyTooLong = MAKERESULT(Module_Settings, 242);
static constexpr Result ResultSettingsItemNameInvalidFormat = MAKERESULT(Module_Settings, 261);
static constexpr Result ResultSettingsItemKeyInvalidFormat = MAKERESULT(Module_Settings, 262);
static constexpr Result ResultSettingsItemValueInvalidFormat = MAKERESULT(Module_Settings, 263);

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
static constexpr u32 Module_ServiceFramework = 10;
static constexpr Result ResultServiceFrameworkTargetNotFound = MAKERESULT(Module_ServiceFramework, 261);
static constexpr Result ResultServiceFrameworkOutOfDomainEntries = MAKERESULT(Module_ServiceFramework, 301);
static constexpr Result ResultServiceFrameworkRequestDeferred = MAKERESULT(Module_ServiceFramework, 811);
static constexpr Result ResultServiceFrameworkRequestDeferredByUser = MAKERESULT(Module_ServiceFramework, 812);

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
static constexpr u32 Module_Sm = 21;
static constexpr Result ResultSmInsufficientProcesses = MAKERESULT(Module_Sm, 1);
static constexpr Result ResultSmInvalidClient = MAKERESULT(Module_Sm, 2);
static constexpr Result ResultSmInsufficientSessions = MAKERESULT(Module_Sm, 3);
static constexpr Result ResultSmAlreadyRegistered = MAKERESULT(Module_Sm, 4);
static constexpr Result ResultSmInsufficientServices = MAKERESULT(Module_Sm, 5);
static constexpr Result ResultSmInvalidServiceName = MAKERESULT(Module_Sm, 6);
static constexpr Result ResultSmNotRegistered = MAKERESULT(Module_Sm, 7);
static constexpr Result ResultSmNotAllowed = MAKERESULT(Module_Sm, 8);
static constexpr Result ResultSmTooLargeAccessControl = MAKERESULT(Module_Sm, 9);

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
static constexpr u32 Module_Spl = 26;
/* Results 1-99 are converted smc results. */
static constexpr Result ResultSplSmcNotImplemented = MAKERESULT(Module_Spl, 1);
static constexpr Result ResultSplSmcInvalidArgument = MAKERESULT(Module_Spl, 2);
static constexpr Result ResultSplSmcInProgress = MAKERESULT(Module_Spl, 3);
static constexpr Result ResultSplSmcNoAsyncOperation = MAKERESULT(Module_Spl, 4);
static constexpr Result ResultSplSmcInvalidAsyncOperation = MAKERESULT(Module_Spl, 5);
static constexpr Result ResultSplSmcBlacklisted = MAKERESULT(Module_Spl, 6);
/* Results 100+ are spl results. */
static constexpr Result ResultSplInvalidSize = MAKERESULT(Module_Spl, 100);
static constexpr Result ResultSplUnknownSmcResult = MAKERESULT(Module_Spl, 101);
static constexpr Result ResultSplDecryptionFailed = MAKERESULT(Module_Spl, 102);
static constexpr Result ResultSplOutOfKeyslots = MAKERESULT(Module_Spl, 104);
static constexpr Result ResultSplInvalidKeyslot = MAKERESULT(Module_Spl, 105);
static constexpr Result ResultSplBootReasonAlreadySet = MAKERESULT(Module_Spl, 106);
static constexpr Result ResultSplBootReasonNotSet = MAKERESULT(Module_Spl, 107);
static constexpr Result ResultSplInvalidArgument = MAKERESULT(Module_Spl, 108);

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
static constexpr u32 Module_Updater = 158;
static constexpr Result ResultUpdaterBootImagePackageNotFound = MAKERESULT(Module_Updater, 2);
static constexpr Result ResultUpdaterInvalidBootImagePackage = MAKERESULT(Module_Updater, 3);
static constexpr Result ResultUpdaterTooSmallWorkBuffer = MAKERESULT(Module_Updater, 4);
static constexpr Result ResultUpdaterMisalignedWorkBuffer = MAKERESULT(Module_Updater, 5);
static constexpr Result ResultUpdaterNeedsRepairBootImages = MAKERESULT(Module_Updater, 6);

View File

@@ -0,0 +1,116 @@
/**
* @file result_utilities.h
* @brief Utilities for handling Results.
* @author SciresM
* @copyright libnx Authors
*/
#pragma once
#include <switch/result.h>
#ifdef __cplusplus
#include <cstdlib>
extern "C" {
#endif
/// Evaluates an expression that returns a result, and returns the result if it would fail.
#define R_TRY(res_expr) \
({ \
const Result _tmp_r_try_rc = res_expr; \
if (R_FAILED(_tmp_r_try_rc)) { \
return _tmp_r_try_rc; \
} \
})
/// Evaluates an expression that returns a result, and fatals the result if it would fail.
#ifdef RESULT_ABORT_ON_ASSERT
#define R_ASSERT_IMPL(res) std::abort()
#else
#define R_ASSERT_IMPL(res) fatalSimple(res)
#endif
#define R_ASSERT(res_expr) \
({ \
const Result _tmp_r_assert_rc = res_expr; \
if (R_FAILED(_tmp_r_assert_rc)) { \
R_ASSERT_IMPL(_tmp_r_assert_rc); \
} \
})
/// Helpers for pattern-matching on a result expression, if the result would fail.
#define R_TRY_CATCH_RESULT _tmp_r_try_catch_rc
#define R_TRY_CATCH(res_expr) \
({ \
const Result R_TRY_CATCH_RESULT = res_expr; \
if (R_FAILED(R_TRY_CATCH_RESULT)) { \
if (false)
#define R_CATCH(catch_result) \
} else if (R_TRY_CATCH_RESULT == catch_result) { \
_Static_assert(R_FAILED(catch_result), "Catch result must be constexpr error Result!"); \
if (false) { } \
else
#define R_GET_CATCH_RANGE_IMPL(_1, _2, NAME, ...) NAME
#define R_CATCH_RANGE_IMPL_2(catch_result_start, catch_result_end) \
} else if (catch_result_start <= R_TRY_CATCH_RESULT && R_TRY_CATCH_RESULT <= catch_result_end) { \
_Static_assert(R_FAILED(catch_result_start), "Catch start result must be constexpr error Result!"); \
_Static_assert(R_FAILED(catch_result_end), "Catch end result must be constexpr error Result!"); \
_Static_assert(R_MODULE(catch_result_start) == R_MODULE(catch_result_end), "Catch range modules must be equal!"); \
if (false) { } \
else
#define R_CATCH_RANGE_IMPL_1(catch_result) R_CATCH_RANGE_IMPL_2(catch_result##RangeStart, catch_result##RangeEnd)
#define R_CATCH_RANGE(...) R_GET_CATCH_RANGE_IMPL(__VA_ARGS__, R_CATCH_RANGE_IMPL_2, R_CATCH_RANGE_IMPL_1)(__VA_ARGS__)
#define R_CATCH_MODULE(module) \
} else if (R_MODULE(R_TRY_CATCH_RESULT) == module) { \
_Static_assert(module != 0, "Catch module must be error!"); \
if (false) { } \
else
#define R_CATCH_ALL() \
} else if (R_FAILED(R_TRY_CATCH_RESULT)) { \
if (false) { } \
else
#define R_END_TRY_CATCH \
else if (R_FAILED(R_TRY_CATCH_RESULT)) { \
return R_TRY_CATCH_RESULT; \
} \
} \
})
/// Evaluates an expression that returns a result, and returns the result (after evaluating a cleanup expression) if it would fail.
#define R_CLEANUP_RESULT _tmp_r_try_cleanup_rc
#define R_TRY_CLEANUP(res_expr, cleanup_expr) \
({ \
const Result R_CLEANUP_RESULT = res_expr; \
if (R_FAILED(R_CLEANUP_RESULT)) { \
({ cleanup_expr }); \
return R_CLEANUP_RESULT; \
} \
})
#ifdef __cplusplus
}
#endif
// For C++, also define R_CATCH_MANY helper.
#ifdef __cplusplus
template<Result... rs>
constexpr inline bool _CheckResultsForMultiTryCatch(const Result &rc) {
static_assert((R_FAILED(rs) && ...), "Multi try catch result must be constexpr error Result!");
return ((rs == rc) || ...);
}
#define R_CATCH_MANY(...) \
} else if (_CheckResultsForMultiTryCatch<__VA_ARGS__>(_tmp_r_try_catch_rc)) { \
if (false) { } \
else
#endif

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
static constexpr u32 Module_Vi = 114;
static constexpr Result ResultViOperationFailed = MAKERESULT(Module_Vi, 1);
static constexpr Result ResultViNotSupported = MAKERESULT(Module_Vi, 6);
static constexpr Result ResultViNotFound = MAKERESULT(Module_Vi, 7);

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "rnd/rnd_api.hpp"

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2018-2019 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 <limits>
#include <switch.h>
namespace sts::rnd {
/* Random utilities. */
void GenerateRandomBytes(void* out, size_t size);
u32 GenerateRandomU32(u32 max = std::numeric_limits<u32>::max());
u64 GenerateRandomU64(u64 max = std::numeric_limits<u64>::max());
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "ro/ro_types.hpp"

View File

@@ -0,0 +1,152 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include <cstdlib>
namespace sts::ro {
enum class ModuleType : u8 {
ForSelf = 0,
ForOthers = 1,
Count
};
struct ModuleId {
u8 build_id[0x20];
};
static_assert(sizeof(ModuleId) == sizeof(LoaderModuleInfo::build_id), "ModuleId definition!");
class NrrHeader {
public:
static constexpr u32 Magic = 0x3052524E;
private:
u32 magic;
u8 reserved_04[0xC];
u64 title_id_mask;
u64 title_id_pattern;
u8 reserved_20[0x10];
u8 modulus[0x100];
u8 fixed_key_signature[0x100];
u8 nrr_signature[0x100];
u64 title_id;
u32 size;
u8 type; /* 7.0.0+ */
u8 reserved_33D[3];
u32 hashes_offset;
u32 num_hashes;
u8 reserved_348[8];
public:
bool IsMagicValid() const {
return this->magic == Magic;
}
bool IsTitleIdValid() const {
return (this->title_id & this->title_id_mask) == this->title_id_pattern;
}
ModuleType GetType() const {
const ModuleType type = static_cast<ModuleType>(this->type);
if (type >= ModuleType::Count) {
std::abort();
}
return type;
}
u64 GetTitleId() const {
return this->title_id;
}
u32 GetSize() const {
return this->size;
}
u32 GetNumHashes() const {
return this->num_hashes;
}
uintptr_t GetHashes() const {
return reinterpret_cast<uintptr_t>(this) + this->hashes_offset;
}
};
static_assert(sizeof(NrrHeader) == 0x350, "NrrHeader definition!");
class NroHeader {
public:
static constexpr u32 Magic = 0x304F524E;
private:
u32 entrypoint_insn;
u32 mod_offset;
u8 reserved_08[0x8];
u32 magic;
u8 reserved_14[0x4];
u32 size;
u8 reserved_1C[0x4];
u32 text_offset;
u32 text_size;
u32 ro_offset;
u32 ro_size;
u32 rw_offset;
u32 rw_size;
u32 bss_size;
u8 reserved_3C[0x4];
ModuleId module_id;
u8 reserved_60[0x20];
public:
bool IsMagicValid() const {
return this->magic == Magic;
}
u32 GetSize() const {
return this->size;
}
u32 GetTextOffset() const {
return this->text_offset;
}
u32 GetTextSize() const {
return this->text_size;
}
u32 GetRoOffset() const {
return this->ro_offset;
}
u32 GetRoSize() const {
return this->ro_size;
}
u32 GetRwOffset() const {
return this->rw_offset;
}
u32 GetRwSize() const {
return this->rw_size;
}
u32 GetBssSize() const {
return this->bss_size;
}
const ModuleId *GetModuleId() const {
return &this->module_id;
}
};
static_assert(sizeof(NroHeader) == 0x80, "NroHeader definition!");
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) 2018-2019 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/>.
*/
/* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++" */
#pragma once
#include <utility>
template<class F>
class ScopeGuard {
private:
F f;
bool active;
public:
ScopeGuard(F f) : f(std::move(f)), active(true) { }
~ScopeGuard() { if (active) { f(); } }
void Cancel() { active = false; }
ScopeGuard() = delete;
ScopeGuard(const ScopeGuard &) = delete;
ScopeGuard& operator=(const ScopeGuard&) = delete;
ScopeGuard(ScopeGuard&& rhs) : f(std::move(rhs.f)), active(rhs.active) {
rhs.Cancel();
}
};
template<class F>
ScopeGuard<F> MakeScopeGuard(F f) {
return ScopeGuard<F>(std::move(f));
}
enum class ScopeGuardOnExit {};
template <typename F>
ScopeGuard<F> operator+(ScopeGuardOnExit, F&& f) {
return ScopeGuard<F>(std::forward<F>(f));
}
#define CONCATENATE_IMPL(S1, s2) s1##s2
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
#ifdef __COUNTER__
#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __COUNTER__)
#else
#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __LINE__)
#endif
#define SCOPE_GUARD ScopeGuardOnExit() + [&]()
#define ON_SCOPE_EXIT auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_) = SCOPE_GUARD

View File

@@ -0,0 +1,83 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "iwaitable.hpp"
#include "ipc.hpp"
#include "utilities.hpp"
template<typename T, auto MakeShared>
class IServer : public IWaitable {
static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject");
protected:
Handle port_handle;
unsigned int max_sessions;
public:
IServer(unsigned int max_s) : port_handle(0), max_sessions(max_s) { }
virtual ~IServer() {
if (port_handle) {
svcCloseHandle(port_handle);
}
}
SessionManagerBase *GetSessionManager() {
return static_cast<SessionManagerBase *>(this->GetManager());
}
/* IWaitable */
virtual Handle GetHandle() override {
return this->port_handle;
}
virtual Result HandleSignaled(u64 timeout) override {
/* If this server's port was signaled, accept a new session. */
Handle session_h;
R_TRY(svcAcceptSession(&session_h, this->port_handle));
this->GetSessionManager()->AddSession(session_h, std::move(ServiceObjectHolder(std::move(MakeShared()))));
return ResultSuccess;
}
};
template <typename T, auto MakeShared = std::make_shared<T>>
class ServiceServer : public IServer<T, MakeShared> {
public:
ServiceServer(const char *service_name, unsigned int max_s) : IServer<T, MakeShared>(max_s) {
DoWithSmSession([&]() {
R_ASSERT(smRegisterService(&this->port_handle, service_name, false, this->max_sessions));
});
}
};
template <typename T, auto MakeShared = std::make_shared<T>>
class ExistingPortServer : public IServer<T, MakeShared> {
public:
ExistingPortServer(Handle port_h, unsigned int max_s) : IServer<T, MakeShared>(max_s) {
this->port_handle = port_h;
}
};
template <typename T, auto MakeShared = std::make_shared<T>>
class ManagedPortServer : public IServer<T, MakeShared> {
public:
ManagedPortServer(const char *service_name, unsigned int max_s) : IServer<T, MakeShared>(max_s) {
R_ASSERT(svcManageNamedPort(&this->port_handle, service_name, this->max_sessions));
}
};

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) 2018-2019 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 "ipc.hpp"
#include "services/dmntcht.h"

View File

@@ -0,0 +1,66 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#ifdef __cplusplus
extern "C" {
#endif
#define AMS_FATAL_ERROR_MAX_STACKTRACE 0x20
#define AMS_FATAL_ERROR_MAX_STACKDUMP 0x100
#define STD_ABORT_ADDR_MAGIC (0x8)
#define STD_ABORT_VALUE_MAGIC (0xA55AF00DDEADCAFEul)
#define DATA_ABORT_ERROR_DESC (0x101)
#define STD_ABORT_ERROR_DESC (0xFFE)
typedef struct {
u32 magic;
u32 error_desc;
u64 title_id;
union {
u64 gprs[32];
struct {
u64 _gprs[29];
u64 fp;
u64 lr;
u64 sp;
};
};
u64 pc;
u64 module_base;
u32 pstate;
u32 afsr0;
u32 afsr1;
u32 esr;
u64 far;
u64 report_identifier; /* Normally just system tick. */
u64 stack_trace_size;
u64 stack_dump_size;
u64 stack_trace[AMS_FATAL_ERROR_MAX_STACKTRACE];
u8 stack_dump[AMS_FATAL_ERROR_MAX_STACKDUMP];
} AtmosphereFatalErrorContext;
Result bpcAmsInitialize(void);
void bpcAmsExit(void);
Result bpcAmsRebootToFatalError(AtmosphereFatalErrorContext *ctx);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,92 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
u64 base;
u64 size;
} DmntMemoryRegionExtents;
typedef struct {
u64 process_id;
u64 title_id;
DmntMemoryRegionExtents main_nso_extents;
DmntMemoryRegionExtents heap_extents;
DmntMemoryRegionExtents alias_extents;
DmntMemoryRegionExtents address_space_extents;
u8 main_nso_build_id[0x20];
} DmntCheatProcessMetadata;
typedef struct {
char readable_name[0x40];
uint32_t num_opcodes;
uint32_t opcodes[0x100];
} DmntCheatDefinition;
typedef struct {
bool enabled;
uint32_t cheat_id;
DmntCheatDefinition definition;
} DmntCheatEntry;
typedef struct {
u64 value;
u8 width;
} DmntFrozenAddressValue;
typedef struct {
u64 address;
DmntFrozenAddressValue value;
} DmntFrozenAddressEntry;
Result dmntchtInitialize(void);
void dmntchtExit(void);
Service* dmntchtGetServiceSession(void);
Result dmntchtHasCheatProcess(bool *out);
Result dmntchtGetCheatProcessEvent(Event *event);
Result dmntchtGetCheatProcessMetadata(DmntCheatProcessMetadata *out_metadata);
Result dmntchtForceOpenCheatProcess(void);
Result dmntchtGetCheatProcessMappingCount(u64 *out_count);
Result dmntchtGetCheatProcessMappings(MemoryInfo *buffer, u64 max_count, u64 offset, u64 *out_count);
Result dmntchtReadCheatProcessMemory(u64 address, void *buffer, size_t size);
Result dmntchtWriteCheatProcessMemory(u64 address, const void *buffer, size_t size);
Result dmntchtQueryCheatProcessMemory(MemoryInfo *mem_info, u64 address);
Result dmntchtGetCheatCount(u64 *out_count);
Result dmntchtGetCheats(DmntCheatEntry *buffer, u64 max_count, u64 offset, u64 *out_count);
Result dmntchtGetCheatById(DmntCheatEntry *out_cheat, u32 cheat_id);
Result dmntchtToggleCheat(u32 cheat_id);
Result dmntchtAddCheat(DmntCheatDefinition *cheat, bool enabled, u32 *out_cheat_id);
Result dmntchtRemoveCheat(u32 cheat_id);
Result dmntchtGetFrozenAddressCount(u64 *out_count);
Result dmntchtGetFrozenAddresses(DmntFrozenAddressEntry *buffer, u64 max_count, u64 offset, u64 *out_count);
Result dmntchtGetFrozenAddress(DmntFrozenAddressEntry *out, u64 address);
Result dmntchtEnableFrozenAddress(u64 address, u64 width, u64 *out_value);
Result dmntchtDisableFrozenAddress(u64 address);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "sm/sm_types.hpp"
#include "sm/sm_api.hpp"
#include "sm/sm_mitm_api.hpp"

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2018-2019 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 "sm_types.hpp"
namespace sts::sm {
/* Ordinary SM API. */
Result GetService(Service *out, ServiceName name);
Result RegisterService(Handle *out, ServiceName name, size_t max_sessions, bool is_light);
Result UnregisterService(ServiceName name);
/* Atmosphere extensions. */
Result HasService(bool *out, ServiceName name);
Result WaitService(ServiceName name);
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2018-2019 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 "sm_types.hpp"
#include "../ncm/ncm_types.hpp"
namespace sts::sm::manager {
/* Manager API. */
Result RegisterProcess(u64 process_id, ncm::TitleId title_id, const void *acid, size_t acid_size, const void *aci, size_t aci_size);
Result UnregisterProcess(u64 process_id);
/* Atmosphere extensions. */
Result EndInitialDefers();
Result HasMitm(bool *out, ServiceName name);
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2018-2019 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 "sm_types.hpp"
#include "../ncm/ncm_types.hpp"
namespace sts::sm::mitm {
/* Mitm API. */
Result InstallMitm(Handle *out_port, Handle *out_query, ServiceName name);
Result UninstallMitm(ServiceName name);
Result DeclareFutureMitm(ServiceName name);
Result AcknowledgeSession(Service *out_service, u64 *out_pid, ncm::TitleId *out_tid, ServiceName name);
Result HasMitm(bool *out, ServiceName name);
Result WaitMitm(ServiceName name);
}

View File

@@ -0,0 +1,142 @@
/*
* Copyright (c) 2018-2019 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 <algorithm>
#include <cstring>
#include <switch.h>
#include "../defines.hpp"
#include "../results.hpp"
namespace sts::sm {
struct ServiceName {
static constexpr size_t MaxLength = 8;
char name[MaxLength];
static constexpr ServiceName Encode(const char *name, size_t name_size) {
ServiceName out{};
for (size_t i = 0; i < MaxLength; i++) {
if (i < name_size) {
out.name[i] = name[i];
} else {
out.name[i] = 0;
}
}
return out;
}
static constexpr ServiceName Encode(const char *name) {
return Encode(name, std::strlen(name));
}
};
static constexpr ServiceName InvalidServiceName = ServiceName::Encode("");
static_assert(alignof(ServiceName) == 1, "ServiceName definition!");
inline bool operator==(const ServiceName &lhs, const ServiceName &rhs) {
return std::memcmp(&lhs, &rhs, sizeof(ServiceName)) == 0;
}
inline bool operator!=(const ServiceName &lhs, const ServiceName &rhs) {
return !(lhs == rhs);
}
/* For Debug Monitor extensions. */
struct ServiceRecord {
ServiceName service;
u64 owner_pid;
u64 max_sessions;
u64 mitm_pid;
u64 mitm_waiting_ack_pid;
bool is_light;
bool mitm_waiting_ack;
};
static_assert(sizeof(ServiceRecord) == 0x30, "ServiceRecord definition!");
/* For process validation. */
static constexpr u64 InvalidProcessId = static_cast<u64>(-1ull);
/* Utility, for scoped access to libnx services. */
template<Result Initializer(), void Finalizer()>
class ScopedServiceHolder {
NON_COPYABLE(ScopedServiceHolder);
private:
Result result;
bool has_initialized;
public:
ScopedServiceHolder(bool initialize = true) : result(ResultSuccess), has_initialized(false) {
if (initialize) {
this->Initialize();
}
}
~ScopedServiceHolder() {
if (this->has_initialized) {
this->Finalize();
}
}
ScopedServiceHolder(ScopedServiceHolder&& rhs) {
this->result = rhs.result;
this->has_initialized = rhs.has_initialized;
rhs.result = ResultSuccess;
rhs.has_initialized = false;
}
ScopedServiceHolder& operator=(ScopedServiceHolder&& rhs) {
rhs.Swap(*this);
return *this;
}
void Swap(ScopedServiceHolder& rhs) {
std::swap(this->result, rhs.result);
std::swap(this->has_initialized, rhs.has_initialized);
}
explicit operator bool() const {
return this->has_initialized;
}
Result Initialize() {
if (this->has_initialized) {
std::abort();
}
DoWithSmSession([&]() {
this->result = Initializer();
});
this->has_initialized = R_SUCCEEDED(this->result);
return this->result;
}
void Finalize() {
if (!this->has_initialized) {
std::abort();
}
Finalizer();
this->has_initialized = false;
}
Result GetResult() const {
return this->result;
}
};
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "spl/spl_types.hpp"
#include "spl/spl_api.hpp"
#include "spl/smc/spl_smc.hpp"

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "../spl_types.hpp"
namespace sts::spl::smc {
/* Helpers for converting arguments. */
inline u32 GetCryptAesMode(CipherMode mode, u32 keyslot) {
return static_cast<u32>((static_cast<u32>(mode) << 4) | (keyslot & 7));
}
inline u32 GetUnwrapEsKeyOption(EsKeyType type, u32 generation) {
return static_cast<u32>((static_cast<u32>(type) << 6) | (generation & 0x3F));
}
/* Functions. */
Result SetConfig(SplConfigItem which, const u64 *value, size_t num_qwords);
Result GetConfig(u64 *out, size_t num_qwords, SplConfigItem which);
Result CheckStatus(Result *out, AsyncOperationKey op);
Result GetResult(Result *out, void *out_buf, size_t out_buf_size, AsyncOperationKey op);
Result ExpMod(AsyncOperationKey *out_op, const void *base, const void *exp, size_t exp_size, const void *mod);
Result GenerateRandomBytes(void *out, size_t size);
Result GenerateAesKek(AccessKey *out, const KeySource &source, u32 generation, u32 option);
Result LoadAesKey(u32 keyslot, const AccessKey &access_key, const KeySource &source);
Result CryptAes(AsyncOperationKey *out_op, u32 mode, const IvCtr &iv_ctr, u32 dst_addr, u32 src_addr, size_t size);
Result GenerateSpecificAesKey(AesKey *out_key, const KeySource &source, u32 generation, u32 which);
Result ComputeCmac(Cmac *out_mac, u32 keyslot, const void *data, size_t size);
Result ReEncryptRsaPrivateKey(void *data, size_t size, const AccessKey &access_key_dec, const KeySource &source_dec, const AccessKey &access_key_enc, const KeySource &source_enc, u32 option);
Result DecryptOrImportRsaPrivateKey(void *data, size_t size, const AccessKey &access_key, const KeySource &source, DecryptOrImportMode mode);
Result SecureExpMod(AsyncOperationKey *out_op, const void *base, const void *mod, SecureExpModMode mode);
Result UnwrapTitleKey(AsyncOperationKey *out_op, const void *base, const void *mod, const void *label_digest, size_t label_digest_size, u32 option);
Result LoadTitleKey(u32 keyslot, const AccessKey &access_key);
Result UnwrapCommonTitleKey(AccessKey *out, const KeySource &source, u32 generation);
/* Deprecated functions. */
Result ImportEsKey(const void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option);
Result DecryptRsaPrivateKey(size_t *out_size, void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option);
Result ImportSecureExpModKey(const void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option);
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2019 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 "spl_types.hpp"
namespace sts::spl {
HardwareType GetHardwareType();
MemoryArrangement GetMemoryArrangement();
bool IsDevelopmentHardware();
bool IsDevelopmentFunctionEnabled();
bool IsMariko();
bool IsRecoveryBoot();
}

View File

@@ -0,0 +1,179 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "../results.hpp"
namespace sts::spl {
namespace smc {
enum class FunctionId : u32 {
SetConfig = 0xC3000401,
GetConfig = 0xC3000002,
CheckStatus = 0xC3000003,
GetResult = 0xC3000404,
ExpMod = 0xC3000E05,
GenerateRandomBytes = 0xC3000006,
GenerateAesKek = 0xC3000007,
LoadAesKey = 0xC3000008,
CryptAes = 0xC3000009,
GenerateSpecificAesKey = 0xC300000A,
ComputeCmac = 0xC300040B,
ReEncryptRsaPrivateKey = 0xC300D60C,
DecryptOrImportRsaPrivateKey = 0xC300100D,
SecureExpMod = 0xC300060F,
UnwrapTitleKey = 0xC3000610,
LoadTitleKey = 0xC3000011,
UnwrapCommonTitleKey = 0xC3000012,
/* Deprecated functions. */
ImportEsKey = 0xC300100C,
DecryptRsaPrivateKey = 0xC300100D,
ImportSecureExpModKey = 0xC300100E,
};
enum class Result {
Success = 0,
NotImplemented = 1,
InvalidArgument = 2,
InProgress = 3,
NoAsyncOperation = 4,
InvalidAsyncOperation = 5,
Blacklisted = 6,
Max = 99,
};
inline ::Result ConvertResult(Result result) {
if (result == Result::Success) {
return ResultSuccess;
}
if (result < Result::Max) {
return MAKERESULT(Module_Spl, static_cast<u32>(result));
}
return ResultSplUnknownSmcResult;
}
enum class CipherMode {
CbcEncrypt = 0,
CbcDecrypt = 1,
Ctr = 2,
};
enum class DecryptOrImportMode {
DecryptRsaPrivateKey = 0,
ImportLotusKey = 1,
ImportEsKey = 2,
ImportSslKey = 3,
ImportDrmKey = 4,
};
enum class SecureExpModMode {
Lotus = 0,
Ssl = 1,
Drm = 2,
};
enum class EsKeyType {
TitleKey = 0,
ElicenseKey = 1,
};
struct AsyncOperationKey {
u64 value;
};
}
enum class HardwareType {
Icosa = 0,
Copper = 1,
Hoag = 2,
Iowa = 3,
};
enum MemoryArrangement {
MemoryArrangement_Standard = 0,
MemoryArrangement_StandardForAppletDev = 1,
MemoryArrangement_StandardForSystemDev = 2,
MemoryArrangement_Expanded = 3,
MemoryArrangement_ExpandedForAppletDev = 4,
/* Note: MemoryArrangement_Dynamic is not official. */
/* Atmosphere uses it to maintain compatibility with firmwares prior to 6.0.0, */
/* which removed the explicit retrieval of memory arrangement from PM. */
MemoryArrangement_Dynamic = 5,
MemoryArrangement_Count,
};
struct BootReasonValue {
union {
struct {
u8 power_intr;
u8 rtc_intr;
u8 nv_erc;
u8 boot_reason;
};
u32 value;
};
};
static_assert(sizeof(BootReasonValue) == sizeof(u32), "BootReasonValue definition!");
#pragma pack(push, 1)
struct AesKey {
union {
u8 data[AES_128_KEY_SIZE];
u64 data64[AES_128_KEY_SIZE / sizeof(u64)];
};
};
static_assert(alignof(AesKey) == alignof(u8), "AesKey definition!");
struct IvCtr {
union {
u8 data[AES_128_KEY_SIZE];
u64 data64[AES_128_KEY_SIZE / sizeof(u64)];
};
};
static_assert(alignof(IvCtr) == alignof(u8), "IvCtr definition!");
struct Cmac {
union {
u8 data[AES_128_KEY_SIZE];
u64 data64[AES_128_KEY_SIZE / sizeof(u64)];
};
};
static_assert(alignof(Cmac) == alignof(u8), "Cmac definition!");
struct AccessKey {
union {
u8 data[AES_128_KEY_SIZE];
u64 data64[AES_128_KEY_SIZE / sizeof(u64)];
};
};
static_assert(alignof(AccessKey) == alignof(u8), "AccessKey definition!");
struct KeySource {
union {
u8 data[AES_128_KEY_SIZE];
u64 data64[AES_128_KEY_SIZE / sizeof(u64)];
};
};
static_assert(alignof(AccessKey) == alignof(u8), "KeySource definition!");
#pragma pack(pop)
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "svc/svc_types.hpp"

View File

@@ -0,0 +1,205 @@
/*
* Copyright (c) 2018-2019 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 <cstring>
#include <switch.h>
#include "../defines.hpp"
#include "../results.hpp"
namespace sts::svc {
/* Debug event types. */
enum class DebugEventType : u32 {
AttachProcess = 0,
AttachThread = 1,
ExitProcess = 2,
ExitThread = 3,
Exception = 4,
};
struct DebugInfoAttachProcess {
u64 title_id;
u64 process_id;
char name[0xC];
u32 flags;
u64 user_exception_context_address; /* 5.0.0+ */
};
struct DebugInfoAttachThread {
u64 thread_id;
u64 tls_address;
u64 entrypoint;
};
enum class ExitProcessReason : u32 {
ExitProcess = 0,
TerminateProcess = 1,
Exception = 2,
};
struct DebugInfoExitProcess {
ExitProcessReason reason;
};
enum class ExitThreadReason : u32 {
ExitThread = 0,
TerminateThread = 1,
ExitProcess = 2,
TerminateProcess = 3,
};
struct DebugInfoExitThread {
ExitThreadReason reason;
};
enum class DebugExceptionType : u32 {
UndefinedInstruction = 0,
InstructionAbort = 1,
DataAbort = 2,
AlignmentFault = 3,
DebuggerAttached = 4,
BreakPoint = 5,
UserBreak = 6,
DebuggerBreak = 7,
UndefinedSystemCall = 8,
SystemMemoryError = 9,
};
struct DebugInfoUndefinedInstructionException {
u32 insn;
};
struct DebugInfoDataAbortException {
u64 address;
};
struct DebugInfoAligntmentFaultException {
u64 address;
};
enum class BreakPointType : u32 {
BreakPoint = 0,
WatchPoint = 1,
};
struct DebugInfoBreakPointException {
BreakPointType type;
u64 address;
};
struct DebugInfoUserBreakException {
u32 break_reason; /* TODO: enum? */
u64 address;
u64 size;
};
struct DebugInfoDebuggerBreakException {
u64 active_thread_ids[4];
};
struct DebugInfoUndefinedSystemCallException {
u32 id;
};
union DebugInfoSpecificException {
DebugInfoUndefinedInstructionException undefined_instruction;
DebugInfoDataAbortException data_abort;
DebugInfoAligntmentFaultException alignment_fault;
DebugInfoBreakPointException break_point;
DebugInfoUserBreakException user_break;
DebugInfoDebuggerBreakException debugger_break;
DebugInfoUndefinedSystemCallException undefined_system_call;
u64 raw;
};
struct DebugInfoException {
DebugExceptionType type;
u64 address;
DebugInfoSpecificException specific;
};
union DebugInfo {
DebugInfoAttachProcess attach_process;
DebugInfoAttachThread attach_thread;
DebugInfoExitProcess exit_process;
DebugInfoExitThread exit_thread;
DebugInfoException exception;
};
struct DebugEventInfo {
DebugEventType type;
u32 flags;
u64 thread_id;
DebugInfo info;
};
static_assert(sizeof(DebugEventInfo) >= 0x40, "DebugEventInfo definition!");
/* Thread State, for svcGetDebugThreadParam. */
enum class ThreadState : u32 {
Waiting = 0,
Running = 1,
Terminated = 4,
Initializing = 5,
};
enum ThreadContextFlag : u32 {
ThreadContextFlag_General = (1 << 0),
ThreadContextFlag_Control = (1 << 1),
ThreadContextFlag_Fpu = (1 << 2),
ThreadContextFlag_FpuControl = (1 << 3),
ThreadContextFlag_All = (ThreadContextFlag_General | ThreadContextFlag_Control | ThreadContextFlag_Fpu | ThreadContextFlag_FpuControl),
};
/* Flags for svcCreateProcess. */
enum CreateProcessFlag : u32 {
/* Is 64 bit? */
CreateProcessFlag_Is64Bit = (1 << 0),
/* What kind of address space? */
CreateProcessFlag_AddressSpaceShift = 1,
CreateProcessFlag_AddressSpaceMask = (7 << CreateProcessFlag_AddressSpaceShift),
CreateProcessFlag_AddressSpace32Bit = (0 << CreateProcessFlag_AddressSpaceShift),
CreateProcessFlag_AddressSpace64BitDeprecated = (1 << CreateProcessFlag_AddressSpaceShift),
CreateProcessFlag_AddressSpace32BitWithoutAlias = (2 << CreateProcessFlag_AddressSpaceShift),
CreateProcessFlag_AddressSpace64Bit = (3 << CreateProcessFlag_AddressSpaceShift),
/* Should JIT debug be done on crash? */
CreateProcessFlag_EnableDebug = (1 << 4),
/* Should ASLR be enabled for the process? */
CreateProcessFlag_EnableAslr = (1 << 5),
/* Is the process an application? */
CreateProcessFlag_IsApplication = (1 << 6),
/* 4.x deprecated: Should use secure memory? */
CreateProcessFlag_DeprecatedUseSecureMemory = (1 << 7),
/* 5.x+ Pool partition type. */
CreateProcessFlag_PoolPartitionShift = 7,
CreateProcessFlag_PoolPartitionMask = (0xF << CreateProcessFlag_PoolPartitionShift),
CreateProcessFlag_PoolPartitionApplication = (0 << CreateProcessFlag_PoolPartitionShift),
CreateProcessFlag_PoolPartitionApplet = (1 << CreateProcessFlag_PoolPartitionShift),
CreateProcessFlag_PoolPartitionSystem = (2 << CreateProcessFlag_PoolPartitionShift),
CreateProcessFlag_PoolPartitionSystemNonSecure = (3 << CreateProcessFlag_PoolPartitionShift),
/* 7.x+ Should memory allocation be optimized? This requires IsApplication. */
CreateProcessFlag_OptimizeMemoryAllocation = (1 << 11),
};
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "updater/updater_types.hpp"
#include "updater/updater_api.hpp"

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "../spl/spl_types.hpp"
#include "updater_types.hpp"
namespace sts::updater {
/* Public API. */
BootImageUpdateType GetBootImageUpdateType(spl::HardwareType hw_type);
Result VerifyBootImagesAndRepairIfNeeded(bool *out_repaired_normal, bool *out_repaired_safe, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
namespace sts::updater {
/* Types. */
enum class BootImageUpdateType {
Erista,
Mariko,
};
enum class BootModeType {
Normal,
Safe,
};
struct VerificationState {
bool needs_verify_normal;
bool needs_verify_safe;
};
/* Convenience size definitions. */
constexpr size_t BctSize = 0x4000;
constexpr size_t EksSize = 0x4000;
constexpr size_t EksEntrySize = 0x200;
constexpr size_t EksBlobSize = 0xB0;
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
#include "util/util_compression.hpp"
#include "util/util_ini.hpp"
#include "util/util_intrusive_list.hpp"
#include "util/util_intrusive_red_black_tree.hpp"

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2018-2019 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 <switch.h>
namespace sts::util {
/* Compression utilities. */
int CompressLZ4(void *dst, size_t dst_size, const void *src, size_t src_size);
/* Decompression utilities. */
int DecompressLZ4(void *dst, size_t dst_size, const void *src, size_t src_size);
}

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