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

74
stratosphere/libstratosphere/.gitignore vendored Normal file
View file

@ -0,0 +1,74 @@
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
*.lst
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Switch Executables
*.nso
*.nro
*.nacp
*.npdm
*.pfs0
*.nsp
*.kip
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
# Distribution files
*.tgz
*.zip
.**/
# NOTE: make sure to make exceptions to this pattern when needed!
*.bin
**/out
**/build

View file

View file

@ -0,0 +1,12 @@
; DO NOT EDIT (unless you know what you are doing)
;
; This subdirectory is a git "subrepo", and this file is maintained by the
; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
;
[subrepo]
remote = https://github.com/Atmosphere-NX/libstratosphere
branch = master
commit = 0c5dab8022aee28e8e22db7ca144471557a18ff2
parent = 3ea9f444dbcd362dc0d07f350dac4898ad98cc83
method = merge
cmdver = 0.4.0

View file

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View file

@ -0,0 +1,152 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
SOURCES := source source/spl source/spl/smc source/updater source/patcher source/map source/rnd source/util source/sm source/cfg source/pm source/hid source/ldr source/kvdb
DATA := data
INCLUDES := include
DEFINES := -DRESULT_ABORT_ON_ASSERT
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(LIBNX)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I. \
-iquote $(CURDIR)/include/switch/
.PHONY: clean all
#---------------------------------------------------------------------------------
all: lib/$(TARGET).a lib/$(TARGET)d.a
lib:
@[ -d $@ ] || mkdir -p $@
release:
@[ -d $@ ] || mkdir -p $@
debug:
@[ -d $@ ] || mkdir -p $@
lib/$(TARGET).a : lib release $(SOURCES) $(INCLUDES)
@$(MAKE) BUILD=release OUTPUT=$(CURDIR)/$@ \
BUILD_CFLAGS="-DNDEBUG=1 -O2" \
DEPSDIR=$(CURDIR)/release \
--no-print-directory -C release \
-f $(CURDIR)/Makefile
lib/$(TARGET)d.a : lib debug $(SOURCES) $(INCLUDES)
@$(MAKE) BUILD=debug OUTPUT=$(CURDIR)/$@ \
BUILD_CFLAGS="-DDEBUG=1 -Og" \
DEPSDIR=$(CURDIR)/debug \
--no-print-directory -C debug \
-f $(CURDIR)/Makefile
dist-bin: all
@tar --exclude=*~ -cjf $(TARGET).tar.bz2 include lib
dist-src:
@tar --exclude=*~ -cjf $(TARGET)-src.tar.bz2 include source Makefile
dist: dist-src dist-bin
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr release debug lib *.bz2
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT) : $(OFILES)
$(OFILES_SRC) : $(HFILES)
#---------------------------------------------------------------------------------
%_bin.h %.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View file

@ -0,0 +1,31 @@
![License](https://img.shields.io/badge/License-GPLv2-blue.svg)
libstratosphere is a work-in-progress C++ library for development of system modules for the Nintendo Switch.
It is built around extending [libnx](https://github.com/switchbrew/libnx).
It also provides bindings for custom extensions to Horizon OS implemented by [Atmosphère](https://github.com/Atmosphere-NX).
Licensing
=====
This software is licensed under the terms of the GPLv2, with exemptions for specific projects noted below.
You can find a copy of the license in the [LICENSE file](LICENSE).
Exemptions:
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the libstratosphere project as GPLv2 or later.
Credits
=====
libstratosphere is currently being developed and maintained by __SciresM__.<br>
In addition to those credited in [Atmosphère's credits](https://github.com/Atmosphere-NX/Atmosphere/blob/master/README.md#Credits), we would like to thank for contributing to libstratosphere in some significant way:
* __hthh__
* __fincs__
* __lioncash__
* __misson20000__
* __neobrain__
* __yellows8__

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"

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