mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-28 20:40:24 +00:00
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:
parent
3ea9f444db
commit
f534d3498e
166 changed files with 20474 additions and 0 deletions
74
stratosphere/libstratosphere/.gitignore
vendored
Normal file
74
stratosphere/libstratosphere/.gitignore
vendored
Normal 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
|
0
stratosphere/libstratosphere/.gitmodules
vendored
Normal file
0
stratosphere/libstratosphere/.gitmodules
vendored
Normal file
12
stratosphere/libstratosphere/.gitrepo
Normal file
12
stratosphere/libstratosphere/.gitrepo
Normal 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
|
339
stratosphere/libstratosphere/LICENSE
Normal file
339
stratosphere/libstratosphere/LICENSE
Normal 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.
|
152
stratosphere/libstratosphere/Makefile
Normal file
152
stratosphere/libstratosphere/Makefile
Normal 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
|
||||
#---------------------------------------------------------------------------------------
|
||||
|
31
stratosphere/libstratosphere/README.md
Normal file
31
stratosphere/libstratosphere/README.md
Normal 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__
|
812
stratosphere/libstratosphere/include/freebsd/sys/tree.h
Normal file
812
stratosphere/libstratosphere/include/freebsd/sys/tree.h
Normal 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_ */
|
53
stratosphere/libstratosphere/include/stratosphere.hpp
Normal file
53
stratosphere/libstratosphere/include/stratosphere.hpp
Normal 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"
|
|
@ -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);
|
||||
}
|
||||
};
|
20
stratosphere/libstratosphere/include/stratosphere/cfg.hpp
Normal file
20
stratosphere/libstratosphere/include/stratosphere/cfg.hpp
Normal 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"
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
139
stratosphere/libstratosphere/include/stratosphere/event.hpp
Normal file
139
stratosphere/libstratosphere/include/stratosphere/event.hpp
Normal 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);
|
||||
}
|
|
@ -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();
|
20
stratosphere/libstratosphere/include/stratosphere/hid.hpp
Normal file
20
stratosphere/libstratosphere/include/stratosphere/hid.hpp
Normal 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"
|
|
@ -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);
|
||||
|
||||
}
|
286
stratosphere/libstratosphere/include/stratosphere/hossynch.hpp
Normal file
286
stratosphere/libstratosphere/include/stratosphere/hossynch.hpp
Normal 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);
|
||||
}
|
||||
};
|
22
stratosphere/libstratosphere/include/stratosphere/ipc.hpp
Normal file
22
stratosphere/libstratosphere/include/stratosphere/ipc.hpp
Normal 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"
|
|
@ -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)) { }
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
};
|
|
@ -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),
|
||||
};
|
||||
};
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
@ -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;
|
||||
}
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
20
stratosphere/libstratosphere/include/stratosphere/ldr.hpp
Normal file
20
stratosphere/libstratosphere/include/stratosphere/ldr.hpp
Normal 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"
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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!");
|
||||
|
||||
}
|
21
stratosphere/libstratosphere/include/stratosphere/map.hpp
Normal file
21
stratosphere/libstratosphere/include/stratosphere/map.hpp
Normal 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"
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
};
|
||||
|
26
stratosphere/libstratosphere/include/stratosphere/mitm.hpp
Normal file
26
stratosphere/libstratosphere/include/stratosphere/mitm.hpp
Normal 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"
|
|
@ -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;
|
||||
};
|
|
@ -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),
|
||||
};
|
||||
};
|
|
@ -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);
|
||||
}
|
|
@ -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),
|
||||
};
|
||||
};
|
||||
};
|
20
stratosphere/libstratosphere/include/stratosphere/ncm.hpp
Normal file
20
stratosphere/libstratosphere/include/stratosphere/ncm.hpp
Normal 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"
|
|
@ -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!");
|
||||
|
||||
}
|
|
@ -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);
|
|
@ -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"
|
|
@ -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);
|
||||
|
||||
}
|
23
stratosphere/libstratosphere/include/stratosphere/pm.hpp
Normal file
23
stratosphere/libstratosphere/include/stratosphere/pm.hpp
Normal 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"
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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!");
|
||||
|
||||
}
|
70
stratosphere/libstratosphere/include/stratosphere/reg.hpp
Normal file
70
stratosphere/libstratosphere/include/stratosphere/reg.hpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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
|
|
@ -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);
|
20
stratosphere/libstratosphere/include/stratosphere/rnd.hpp
Normal file
20
stratosphere/libstratosphere/include/stratosphere/rnd.hpp
Normal 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"
|
|
@ -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());
|
||||
|
||||
}
|
20
stratosphere/libstratosphere/include/stratosphere/ro.hpp
Normal file
20
stratosphere/libstratosphere/include/stratosphere/ro.hpp
Normal 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"
|
|
@ -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!");
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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));
|
||||
}
|
||||
};
|
|
@ -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"
|
|
@ -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
|
|
@ -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
|
22
stratosphere/libstratosphere/include/stratosphere/sm.hpp
Normal file
22
stratosphere/libstratosphere/include/stratosphere/sm.hpp
Normal 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"
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
22
stratosphere/libstratosphere/include/stratosphere/spl.hpp
Normal file
22
stratosphere/libstratosphere/include/stratosphere/spl.hpp
Normal 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"
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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)
|
||||
|
||||
}
|
20
stratosphere/libstratosphere/include/stratosphere/svc.hpp
Normal file
20
stratosphere/libstratosphere/include/stratosphere/svc.hpp
Normal 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
Loading…
Reference in a new issue