diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 000000000..4b4dfad1e
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,25 @@
+---
+name: Bug Report
+about: Something doesn't work correctly in Atmosphère.
+
+---
+
+## Bug Report
+
+### What's the issue you encountered?
+
+### How can the issue be reproduced?
+
+### Crash report?
+
+(If a crash report was created under /atmosphere/crash_reports/, please upload it to
+[gist](https://gist.github.com/) and paste the link here.)
+
+### Environment?
+
+- What bootloader (fusee, hekate, etc) was Atmosphère launched by:
+- Official release or unofficial build:
+- Do you have additional kips you're loading:
+- Additional info about your environment:
+
+### Additional context?
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 000000000..3b93d9d25
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,12 @@
+---
+name: Feature Request
+about: You want to suggest a new feature for Atmosphère.
+
+---
+
+## Feature Request
+
+### What feature are you suggesting?
+
+### Why would this feature be useful?
+
diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md
new file mode 100644
index 000000000..6cc1915b9
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/question.md
@@ -0,0 +1,12 @@
+---
+name: Question
+about: Please ask questions in the ReSwitched discord, instead of making issues.
+
+---
+
+We would like to use GitHub to keep track of problems/feature requests.
+
+If you have a question, please join the ReSwitched discord for help.
+
+- Discord link: https://discordapp.com/invite/DThbZ7z
+
diff --git a/.gitignore b/.gitignore
index e39d9f114..f55c55a62 100644
--- a/.gitignore
+++ b/.gitignore
@@ -61,6 +61,10 @@ Module.symvers
Mkfile.old
dkms.conf
+# Distribution files
+*.tgz
+*.zip
+
.**/
# NOTE: make sure to make exceptions to this pattern when needed!
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 000000000..5f85b18b6
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "common/include/boost"]
+ path = common/include/boost
+ url = https://github.com/Atmosphere-NX/ext-boost.git
diff --git a/Makefile b/Makefile
index 014cb646c..c10b6eb7a 100644
--- a/Makefile
+++ b/Makefile
@@ -1,10 +1,55 @@
-TOPTARGETS := all clean
+TOPTARGETS := all clean dist
+AMSREV := $(shell git rev-parse --short HEAD)
+ifneq (, $(strip $(shell git status --porcelain 2>/dev/null)))
+ AMSREV := $(AMSREV)-dirty
+endif
-all: fusee
-fusee:
+all: fusee stratosphere exosphere thermosphere
+
+thermosphere:
+ $(MAKE) -C thermosphere all
+
+exosphere: thermosphere
+ $(MAKE) -C exosphere all
+
+stratosphere: exosphere
+ $(MAKE) -C stratosphere all
+
+fusee: exosphere stratosphere
$(MAKE) -C $@ all
clean:
$(MAKE) -C fusee clean
+ rm -rf out
+
+dist: all
+ $(eval MAJORVER = $(shell grep '\ATMOSPHERE_RELEASE_VERSION_MAJOR\b' common/include/atmosphere/version.h \
+ | tr -s [:blank:] \
+ | cut -d' ' -f3))
+ $(eval MINORVER = $(shell grep '\ATMOSPHERE_RELEASE_VERSION_MINOR\b' common/include/atmosphere/version.h \
+ | tr -s [:blank:] \
+ | cut -d' ' -f3))
+ $(eval MICROVER = $(shell grep '\ATMOSPHERE_RELEASE_VERSION_MICRO\b' common/include/atmosphere/version.h \
+ | tr -s [:blank:] \
+ | cut -d' ' -f3))
+ $(eval AMSVER = $(MAJORVER).$(MINORVER).$(MICROVER)-$(AMSREV))
+ rm -rf atmosphere-$(AMSVER)
+ rm -rf out
+ mkdir atmosphere-$(AMSVER)
+ mkdir atmosphere-$(AMSVER)/atmosphere
+ mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036
+ mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032
+ cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/fusee-secondary.bin
+ cp common/defaults/BCT.ini atmosphere-$(AMSVER)/BCT.ini
+ cp common/defaults/loader.ini atmosphere-$(AMSVER)/atmosphere/loader.ini
+ cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036/exefs.nsp
+ cp stratosphere/set_mitm/set_mitm.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/exefs.nsp
+ touch atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/boot2.flag
+ cd atmosphere-$(AMSVER); zip -r ../atmosphere-$(AMSVER).zip ./*; cd ../;
+ rm -r atmosphere-$(AMSVER)
+ mkdir out
+ mv atmosphere-$(AMSVER).zip out/atmosphere-$(AMSVER).zip
+ cp fusee/fusee-primary/fusee-primary.bin out/fusee-primary.bin
+
.PHONY: $(TOPTARGETS) fusee
diff --git a/common/defaults/BCT.ini b/common/defaults/BCT.ini
new file mode 100644
index 000000000..3a8600340
--- /dev/null
+++ b/common/defaults/BCT.ini
@@ -0,0 +1,5 @@
+BCT0
+[stage1]
+stage2_path = fusee-secondary.bin
+stage2_addr = 0xF0000000
+stage2_entrypoint = 0xF0000000
\ No newline at end of file
diff --git a/common/defaults/loader.ini b/common/defaults/loader.ini
new file mode 100644
index 000000000..1049ef9dd
--- /dev/null
+++ b/common/defaults/loader.ini
@@ -0,0 +1,4 @@
+[config]
+hbl_tid=010000000000100D
+hbl_path=atmosphere/hbl.nsp
+override_key=!R
\ No newline at end of file
diff --git a/exosphere/src/version.h b/common/include/atmosphere/version.h
similarity index 76%
rename from exosphere/src/version.h
rename to common/include/atmosphere/version.h
index 99200b843..9ab1693ad 100644
--- a/exosphere/src/version.h
+++ b/common/include/atmosphere/version.h
@@ -14,10 +14,11 @@
* along with this program. If not, see .
*/
-#ifndef EXOSPHERE_VERSION_H
-#define EXOSPHERE_VERSION_H
+#ifndef ATMOSPHERE_VERSION_H
+#define ATMOSPHERE_VERSION_H
-#define EXOSPHERE_RELEASE_VERSION_MAJOR 0
-#define EXOSPHERE_RELEASE_VERSION_MINOR 1
+#define ATMOSPHERE_RELEASE_VERSION_MAJOR 0
+#define ATMOSPHERE_RELEASE_VERSION_MINOR 7
+#define ATMOSPHERE_RELEASE_VERSION_MICRO 3
#endif
\ No newline at end of file
diff --git a/common/include/boost b/common/include/boost
new file mode 160000
index 000000000..fc6429e46
--- /dev/null
+++ b/common/include/boost
@@ -0,0 +1 @@
+Subproject commit fc6429e46398e16178b828a3a20e1bee9c56443d
diff --git a/docs/building.md b/docs/building.md
new file mode 100644
index 000000000..47d230b8a
--- /dev/null
+++ b/docs/building.md
@@ -0,0 +1,6 @@
+# Building Atmosphère
+The process for building Atmosphère is similar to building Fusée Gelée payloads and other Switch apps.
+
+In order to build Atmosphère you must have devkitARM and devkitA64 installed on your computer. You can find instructions on how to install and setup devkitARM and devkitA64 on various OSes [here](https://devkitpro.org/wiki/Getting_Started).
+
+Once you have finished installing devkitARM and devkitA64, simply clone the Atmosphère repo, change to it and run `make`.
diff --git a/docs/changelog.md b/docs/changelog.md
new file mode 100644
index 000000000..7aefb2474
--- /dev/null
+++ b/docs/changelog.md
@@ -0,0 +1,79 @@
+# Changelog
+## 0.7.3
++ Loader and fs.mitm now try to reload loader.ini before reading it. This allows for changing the override button combination/HBL title id at runtime.
++ Added a MitM between set:sys and qlaunch, used to override the system version string displayed in system settings.
+ + The displayed system version will now display ` (AMS ..)`.
++ General system stability improvements to enhance the user's experience.
+## 0.7.2
++ Fixed a bug in fs.mitm's LayeredFS read implementation that caused some games to crash when trying to read files.
++ Fixed a bug affecting 1.0.0 that caused games to crash with fatal error 2001-0106 on boot.
++ Improved filenames output by the make dist target.
++ General system stability improvements to enhance the user's experience.
+
+## 0.7.1
++ Fixed a bug preventing consoles on 4.0.0-4.1.0 from going to sleep and waking back up.
++ Fixed a bug preventing consoles on < 4.0.0 from booting without specific KIPs on the SD card.
++ An API was added to Atmosphère's Service Manager for deferring acquisition of all handles for specific services until after early initialization is completed.
++ General system stability improvements to enhance the user's experience.
+
+## 0.7.0
++ First official release of Atmosphère.
++ Supports the following featureset:
+ + Fusée, a custom bootloader.
+ + Supports loading/customizing of arbitrary KIPs from the SD card.
+ + Supports loading a custom kernel from the SD card ("/atmosphere/kernel.bin").
+ + Supports compile-time defined kernel patches on a per-firmware basis.
+ + All patches at paths like /atmosphere/kip_patches//.ips will be applied to the relevant KIPs, allowing for easy distribution of patches supporting multiple versions.
+ + Both the IPS and IPS32 formats are supported.
+ + All patches at paths like /atmosphere/kernel_patches//.ips will be applied to the kernel, allowing for easy distribution of patches supporting multiple versions.
+ + Both the IPS and IPS32 formats are supported.
+ + Configurable by editing BCT.ini on the SD card.
+ + Atmosphère should also be launchable by the alternative hekate bootloader, for those who prefer it.
+ + Exosphère, a fully-featured custom secure monitor.
+ + Exosphere is a re-implementation of Nintendo's TrustZone firmware, fully replicating all of its features.
+ + In addition, it has been extended to provide information on current Atmosphere API version, for homebrew wishing to make use of it.
+ + Stratosphère, a set of custom system modules. This includes:
+ + A loader system module.
+ + Reimplementation of Nintendo's loader, fully replicating all original functionality.
+ + Configurable by editing /atmosphere/loader.ini
+ + First class support for the Homebrew Loader.
+ + An exefs NSP (default "/atmosphere/hbl.nsp") will be used in place of the victim title's exefs.
+ + By default, HBL will replace the album applet, but any application should also be supported.
+ + Extended to support arbitrary redirection of executable content to the SD card.
+ + Files will be preferentially loaded from /atmosphere/titles//exefs/, if present.
+ + Files present in the original exefs a user wants to mark as not present may be "stubbed" by creating a .stub file on the SD.
+ + If present, a PFS0 at /atmosphere/titles//exefs.nsp will fully replace the original exefs.
+ + Redirection is optionally toggleable by holding down certain buttons (by default, holding R disables redirection).
+ + Full support for patching NSO content is implemented.
+ + All patches at paths like /atmosphere/exefs_patches//.ips will be applied, allowing for easy distribution of patches supporting multiple firmware versions and/or titles.
+ + Both the IPS and IPS32 formats are supported.
+ + Extended to support launching content from loose executable files on the SD card, without requiring any official installation.
+ + This is done by specifying FsStorageId_None on launch.
+ + A service manager system module.
+ + Reimplementation of Nintendo's service manager, fully replicating all original functionality.
+ + Compile-time support for reintroduction of "smhax", allowing clients to optionally skip service access verification by skipping initialization.
+ + Extended to allow homebrew to acquire more handles to privileged services than Nintendo natively allows.
+ + Extended to add a new API for installing Man-In-The-Middle listeners for arbitrary services.
+ + API can additionally be used to safely detect whether a service has been registered in a non-blocking way with no side-effects.
+ + Full API documentation to come.
+ + A process manager system module.
+ + Reimplementation of Nintendo's process manager, fully replicating all original functionality.
+ + Extended to allow homebrew to acquire handles to arbitrary processes, and thus read/modify system memory without blocking execution.
+ + Extended to allow homebrew to retrieve information about system resource limits.
+ + Extended by embedding a full, extended implementation of Nintendo's boot2 system module.
+ + Title launch order has been optimized in order to grant access to the SD card faster.
+ + The error-collection system module is intentionally not launched, preventing many system telemetry error reports from being generated at all.
+ + Users may place their own custom sysmodules on the SD card and flag them for automatic boot2 launch by creating a /atmosphere/titles//boot2.flag file on their SD card.
+ + A custom fs.mitm system module.
+ + Uses Atmosphère's MitM API in order to provide an easy means for users to modify game content.
+ + Intercepts all FS commands sent by games, with special handling for commands used to mount RomFS/DLC content to enable easy creation and distribution of game/DLC mods.
+ + fs.mitm will parse the base RomFS image for a game, a RomFS image located at /atmosphere/titles//romfs.bin, and all loose files in /atmosphere/titles//romfs/, and merge them together into a single RomFS image.
+ + When merging, loose files are preferred to content in the SD card romfs.bin image, and files from the SD card image are preferred to those in the base image.
+ + Can additionally be used to intercept commands sent by arbitrary system titles (excepting those launched before SD card is active), by creating a /atmosphere/titles//fsmitm.flag file on the SD card.
+ + Can be forcibly disabled for any title, by creating a /atmosphere/titles//fsmitm_disable.flag file on the SD card.
+ + Redirection is optionally toggleable by holding down certain buttons (by default, holding R disables redirection).
+ + A custom crash report system module.
+ + Serves as a drop-in replacement for Nintendo's own creport system module.
+ + Generates detailed, human-readable reports on system crashes, saving to /atmosphere/crash_reports/_.log.
+ + Because reports are not sent to the erpt sysmodule, this disables all crash report related telemetry.
+ + General system stability improvements to enhance the user's experience.
diff --git a/docs/components/exosphere.md b/docs/components/exosphere.md
new file mode 100644
index 000000000..b8a1103c8
--- /dev/null
+++ b/docs/components/exosphere.md
@@ -0,0 +1,10 @@
+# Exosphère
+Exosphère is a reimplementation of Arm's TrustZone (TZ), also known as Secure Monitor (Secure_Monitor.bin). It has the highest privilege mode available on the Switch’s processor, and has access to everything on the console.
+
+Exosphère will potentially play a big role in Jamais Vu and Déja Vu, which are upcoming software exploits for the Switch, allowing one to launch Atmosphère on a Fusée-Gélee patched (ipatched) Switch console, and will also enable one to launch into CFW directly from the Switch itself without the use of any sort of external device, such as a computer or RCM jig, provided they are on a low enough system firmware.
+
+## TrustZone/Secure Monitor
+TrustZone is responsible for all the cryptographic operations on the Switch. The idea behind the way it operates is that all the keys stay in the TrustZone, and userspace only gets "handles" to them. This would make sure that keydata never leaks and is kept secure. It also has a few more responsibilities, such as power management, providing a source of random numbers, and providing access to various pieces of information that are stored in the fuses.
+
+## Extensions
+Exosphère currently only contains one extension, an SMC allowing homebrew to find which version of Atmosphère is currently running, in order to find out what extensions are allowed to be used.
diff --git a/docs/components/fusee/BCT.md b/docs/components/fusee/BCT.md
new file mode 100644
index 000000000..35c9f8246
--- /dev/null
+++ b/docs/components/fusee/BCT.md
@@ -0,0 +1,26 @@
+# BCT.ini
+BCT.ini is the configuration file used by fusée-primary and fusée-secondary. It is read by fusee-primary.bin to setup and boot fusee-secondary.bin and is also read by fusee-secondary.bin to configure Exosphère or to specify the environment it should boot.
+
+## Configuration
+This file is located at the root of your SD.
+```
+BCT0
+[stage1]
+stage2_path = fusee-secondary.bin
+stage2_addr = 0xF0000000
+stage2_entrypoint = 0xF0000000
+```
+Add the following lines and replace the `X` according to the following list if you have trouble booting past the firmware version detection.
+`target_firmware` is the OFW major version.
+```
+[exosphere]
+target_firmware = X
+```
+```
+1.0.0 = 1
+2.X.X = 2
+3.X.X = 3
+4.X.X = 4
+5.X.X = 5
+6.0.0 = 6
+```
diff --git a/docs/components/fusee/fusee.md b/docs/components/fusee/fusee.md
new file mode 100644
index 000000000..64c9575bd
--- /dev/null
+++ b/docs/components/fusee/fusee.md
@@ -0,0 +1,20 @@
+# Fusée
+Fusée (not to be confused with Fusée Gelée) is a custom bootloader needed to start Atmosphère and replaces Nintendo's Package1loader/bootloader. It currently utilizes the [Tegra X1 RCM Vulnerability](https://nvidia.custhelp.com/app/answers/detail/a_id/4660/~/security-notice%3A-nvidia-tegra-rcm-vulnerability) in order to function.
+
+Fusée is split into two separate parts: fusée-primary and fusée-secondary. This is due to the RCM Vulnerability only allowing payloads of a limited filesize to be sent to the device.
+
+As of June 2018, there are new Switch systems being sold that prevent Fusée (or any payload that requires the Fusée Gelée exploit) from working due to having an ipatched bootrom. All ipatched systems share the HAC-S-JXE-C3 product code. While Fusée cannot work on these ipatched units, they still come on firmware 4.1.0, which is vulnerable to the upcoming Déja Vu software exploit. Note that if you update past 4.1.0 on one of these ipatched units, your odds of being able to install Atmosphère or run any homebrew become practically non-existent.
+
+Additionally, a hardware revision of the Switch known as “Mariko” is believed to be in development. No such units have been seen in stores yet, but it is expected Nintendo will roll them out silently. The Mariko units will most likely patch the bootrom vulnerability Fusée Gelée, which is currently used to access CFW, and will likely have their own proprietary bootloader.
+
+## Fusée-Primary
+Fusée-primary is the payload file (fusee-primary.bin) sent to the Switch from an external device. Once sent, fusée-primary makes initial preparations before loading fusée-secondary from the Switch’s SD Card.
+
+Fusée-primary can be configured via the [BCT.ini](../fusee/BCT.md) file located on the Switch’s SD card.
+
+## Fusée-Secondary
+Fusée-secondary is a payload file that stays on the root of the Switch’s SD Card (fusee-secondary.bin). It is automatically launched once fusée-primary has finished, and is responsible for preparing the Switch’s hardware for future running environments, such as the homebrew menu. Fusée-secondary is also responsible for validating and launching Exosphère.
+
+Fusée-secondary contains various [.kip modules](/docs/main.md#modules). These modules modify existing features in the OS, and can also add new ones.
+
+Fusée is also capable of chainloading other payloads such as Linux.
diff --git a/docs/components/stratosphere.md b/docs/components/stratosphere.md
new file mode 100644
index 000000000..58813ab77
--- /dev/null
+++ b/docs/components/stratosphere.md
@@ -0,0 +1,10 @@
+# Stratosphère
+Stratosphère allows customization of the Horizon OS and Switch kernel. It includes custom sysmodules that extend the kernel and provide new features. It also includes a reimplementation of the loader sysmodules to hook important system actions.
+
+The sysmodules that Stratosphère includes are:
++ [boot](../modules/boot.md): This module boots the system and initalizes hardware.
++ [creport](../modules/creport.md): Reimplementation of Nintendo’s crash report system. Dumps all error logs to the SD card instead of saving them to the NAND and sending them to Nintendo.
++ [fs_mitm](../modules/fs_mitm.md): This module can log, deny, delay, replace, and redirect any request made to the File System.
++ [loader](../modules/loader.md): Enables modifying the code of binaries that are not stored inside the kernel.
++ [pm](../modules/pm.md): Reimplementation of Nintendo’s Process Manager.
++ [sm](../modules/sm.md): Reimplementation of Nintendo’s Service Manager.
diff --git a/docs/components/thermosphere.md b/docs/components/thermosphere.md
new file mode 100644
index 000000000..c29483598
--- /dev/null
+++ b/docs/components/thermosphere.md
@@ -0,0 +1,6 @@
+# Thermosphère
+Thermosphère is a hypervisor based implementation of emuNAND. An emuNAND is a copy of the firmware on the Switch’s internal memory (sysNAND), and is typically installed on an external SD Card.
+
+An emuNAND operates completely independently of the sysNAND. This allows one to make or test various modifications and homebrew safely without needing to restore their NAND backup afterwards by testing things on the emuNAND, and switching back to the sysNAND when finished. In the case of past Nintendo systems such as the 3DS, an emuNAND could also be used to update your system to the latest firmware while keeping your sysNAND on a lower version, however this may be more difficult to do on the Switch due to Nintendo using efuse technology for major system updates.
+
+Thermosphère is currently planned to be included in the 1.0 release of Atmosphère.
diff --git a/docs/components/troposphere.md b/docs/components/troposphere.md
new file mode 100644
index 000000000..2664843d8
--- /dev/null
+++ b/docs/components/troposphere.md
@@ -0,0 +1,2 @@
+# Troposphère
+Troposphère contains various application-level modifications to the OS, such as launching homebrew directly from the homemenu or executing cheat/gameshark codes, similar to Luma3DS. Troposphère is not yet implemented in Atmosphère.
diff --git a/docs/main.md b/docs/main.md
new file mode 100644
index 000000000..02f3efc39
--- /dev/null
+++ b/docs/main.md
@@ -0,0 +1,29 @@
+# Atmosphère
+Atmosphère is a work-in-progress customized firmware for the Nintendo Switch. Atmosphère consists of several different components, each in charge of performing different system functions of the Nintendo Switch.
+
+The components of Atmosphère are:
++ [Fusée](../docs/components/fusee/fusee.md), a custom bootloader.
++ [Exosphère](../docs/components/exosphere.md), a fully-featured custom secure monitor.
++ [Stratosphère](../docs/components/stratosphere.md), a set of custom system modules.
++ [Thermosphère](../docs/components/thermosphere.md), a hypervisor-based emuNAND implementation. This component has not been implemented yet.
++ [Troposphère](../docs/components/troposphere.md), Application-level patches to the Horizon OS. This component has also not been implemented yet.
+
+### Modules
+The Stratosphère component of Atmosphère contains various modules. These have a `.kip` extension. They provide custom features, extend existing features, or replace Nintendo sysmodules.
+
+Stratosphère's modules include:
++ [boot](../docs/modules/boot.md)
++ [creport](../docs/modules/creport.md)
++ [fs_mitm](../docs/modules/fs_mitm.md)
++ [loader](../docs/modules/loader.md)
++ [pm](../docs/modules/pm.md)
++ [sm](../docs/modules/sm.md)
+
+### Building Atmosphère
+A guide to building Atmosphère can be found [here](../docs/building.md).
+
+### Upcoming Features
+A list of planned features for Atmosphère can be found [here](../docs/roadmap.md).
+
+### Release History
+A changelog of previous versions of Atmosphère can be found [here](../docs/changelog.md).
diff --git a/docs/modules/boot.md b/docs/modules/boot.md
new file mode 100644
index 000000000..53013b32c
--- /dev/null
+++ b/docs/modules/boot.md
@@ -0,0 +1,2 @@
+# boot
+The boot module is responsible for booting the system and initalizing hardware. A second boot module known as boot2 is integrated with the [pm (process manager)](../modules/pm.md) sysmodule in Atmosphère, and launches other processes.
diff --git a/docs/modules/creport.md b/docs/modules/creport.md
new file mode 100644
index 000000000..a6a996f43
--- /dev/null
+++ b/docs/modules/creport.md
@@ -0,0 +1,2 @@
+# creport
+creport is a reimplementation of Nintendo's crash reporter. Atmosphère's creport catches all error logs that would have been saved to the NAND and instead saves them to the SD card for debugging purposes. This is helpful because the errors no longer go to Nintendo and developers of homebrew can still see the errors to help with the debugging process. creport catches system errors, game crashes, and homebrew crashes.
diff --git a/docs/modules/fs_mitm.md b/docs/modules/fs_mitm.md
new file mode 100644
index 000000000..345c42275
--- /dev/null
+++ b/docs/modules/fs_mitm.md
@@ -0,0 +1,2 @@
+# fs_mitm
+fs_mitm is a sysmodule that enables intercepting file system operations. This module can log, deny, delay, replace, or redirect any request made to the filesystem. It enables LayeredFS to function, which allows for game mods.
diff --git a/docs/modules/loader.md b/docs/modules/loader.md
new file mode 100644
index 000000000..6bde97faa
--- /dev/null
+++ b/docs/modules/loader.md
@@ -0,0 +1,68 @@
+# loader
+
+loader is a reimplementation of the loader sysmodule. This module is responsible for creating processes from executable NSO images and registering their access control with the kernel, sm, and fs.
+
+## Atmosphère Extensions
+
+Atmosphère extends this module to allow executables to be replaced or patched by files stored on the SD card. Note that a few services are required for SD card access and therefore cannot be replaced or patched in this manner. This includes psc, bus, and pcv.
+
+### Exefs Replacement
+
+TODO: details on buttons affecting this.
+
+When a process is created, loader will search for several NSO filenames in the title's exefs directory.
+These filenames are, in this order:
+
+ - rtld
+ - main
+ - subsdk0
+ - subsdk1
+ - ...
+ - subsdk9
+ - sdk
+
+Each NSO that is found will be loaded into the process contiguously. The process's entrypoint is at the first NSO to be loaded, usually `rtld` or `main`.
+
+Additionally, when a process is loaded, loader will search for a `main.npdm` file in the exefs directory specifying the title's permissions.
+
+Atmosphère extends this functionality by also searching for these files on the SD card. When searching for a file, loader will first check if it exists on the SD card. If it does, that file will be used instead. Otherwise, it will use the copy located in the exefs, if that is present. The following directory will be searched.
+
+```
+sdmc:/atmosphere/titles//exefs/
+```
+
+This allows the replacement of applets, sysmodules, or even games with homebrew versions.
+
+In order to prevent an NSO from being loaded even if it exists in the exefs, loader will also check if a stub file exists. If such a file exists, the NSO will not be loaded. The files should be named like `rtld.stub`, `main.stub`, etc. and may be empty.
+
+### NSO Patching
+
+TODO: details on buttons affecting this.
+
+When an NSO is loaded, the stratosphere implementatin of loader will search for IPS patch files on the SD card in the following locations.
+```
+sdmc:/atmosphere/exefs_patches//.ips
+```
+This organization allows patchsets affecting multiple NSOs to be distributed as a single directory. Patches will be searched for in each patchset directory. The name of each patch file should match the hexadecimal build ID of the NSO to affect, except that trailing zero bytes may be left off. Because the NSO build ID is unique for every NSO, this means patches will only apply to the files they are meant to apply to.
+
+Patch files are accepted in either IPS format or IPS32 format.
+
+Because NSO files are compressed, patch files are not made between the original version of a compressed NSO and the modified version of such an NSO. Instead, they are made between the uncompressed version of an NSO and the modified (and still uncompressed) version of that NSO. This also means that a patch file cannot be manually applied to the compressed version of an NSO; it must be applied to the uncompressed version. The Stratosphere implementation of loader will correctly apply these patches while loading the process regardless of whether the NSO it finds is compressed or not.
+
+When authoring patches, [hactool](https://github.com/SciresM/hactool) can be used to find an NSO's build ID and to uncompress NSOs. Recent versions of the [ReSwitched IDA loaders](https://github.com/reswitched/loaders) can be used to load uncompressed NSOs into IDA in such a way that you can [apply patches to the input file](https://www.hex-rays.com/products/ida/support/idadoc/1618.shtml). From there, any IPS tool can be used to create the patch between the original NSO and the patched NSO. Note that if the NSO you are patching is larger than 16 MiB, you will have to use a tool that supports IPS32.
+
+### HBL Support
+
+Atmosphère can use the loader module in order to turn any game on your Switch's home menu into a launchpoint for the Homebrew Menu, rather than launching it through the album applet. This allows one to launch the Homebrew Menu with access to the ~3.2GB of RAM that the Switch reserves for games and applications, as opposed to the 442MB of RAM we are limited to when launching the Homebrew Menu from the album. This also means that it is no longer necessary to install homebrew as `.nsp` files on your Switch so long as you are using this method, as the only reason to do so is to allow the homebrew to access all of the Switch's available memory.
+
+In order to setup this method you will need the latest release of [hbmenu](https://github.com/switchbrew/nx-hbmenu/releases), and the latest release of [hbloader](https://github.com/switchbrew/nx-hbloader/releases). Place `hbmenu.nro` on the root of your Switch's SD Card, and place `hbl.nsp` in the atmosphere folder. From there, simply configure `loader.ini` in the atmosphere folder by replacing the Title ID in the ini (hbl_tid) (it is the Title ID for the album by default) with the Title ID of whatever game you wish to use to launch the Homebrew Menu. A list of Title IDs for Switch Games can be found [here](https://switchbrew.org/wiki/Title_list/Games). Afterwards you may reinsert your SD Card into your Switch and boot into Atmosphère as you normally would. You should now be able to boot into the Homebrew Menu by launching your designated game of choice.
+
+### Button Overrides
+
+By default `loader.ini` is configured to launch the Homebrew Menu when launching the game normally, and launching the game when selecting the game while holding down R. If you wish to change this, you can modify the override_key section of `loader.ini`. Placing an exclamation point in front of whatever button you wish to use will make it so that you will only launch the actual game while holding down that button, otherwise you will go into the Homebrew Menu. Removing the exclamation point will reverse this, meaning that you will boot into the Homebrew Menu only while holding down the assigned button when launching the game.
+
+For example, `override_key=!R` will run the game only while holding down R when launching it, otherwise it will boot into the Homebrew Menu. `override_key=R` will only boot into the Homebrew Menu while holding down R when launching the game, otherwise it will launch the game as normal.
+
+### SM MITM Integration
+
+When the Stratosphere implementation of loader creates a new process, it notifies [sm](sm.md) through the `AtmosphereAssociatePidTidForMitm` command to notify any MITM services of new processes' identities.
diff --git a/docs/modules/pm.md b/docs/modules/pm.md
new file mode 100644
index 000000000..fe00ee18a
--- /dev/null
+++ b/docs/modules/pm.md
@@ -0,0 +1,23 @@
+# pm
+
+pm is a reimplementation of Nintendo's process manager. This module is responsible for tracking running processes on the system, and managing resource limits. pm is also required to create and manage processes for homebrew applications.
+
+## Atmosphère Extensions
+
+There are a few ways in which the Stratosphere implementation of pm differs intentionally from the stock pm.
+
+### IPC: AtmosphereGetProcessHandle
+
+The Stratosphere implementation of pm adds an additional command to the [`pm:dmnt`](https://reswitched.github.io/SwIPC/ifaces.html#nn::pm::detail::IDebugMonitorInterface) interface, called `AtmosphereGetProcessHandle`. Its command ID is `65000` on all system firmware versions. It takes a `u64 process_id` and returns a process handle for the specified process, if that process is known. Notable exceptions include KIPs, which are not known to pm. If the specified process cannot be found, error code 0x20F is returned.
+
+The SwIPC definition for this command follows.
+```
+interface nn::pm::detail::IDebugMonitorInterface is pm:dmnt {
+ ...
+ [65000] AtmosphereGetProcessHandle(u64 pid) -> handle process_handle;
+}
+```
+
+### Extra System Memory for Sysmodules
+
+The Stratosphere implementation of pm shrinks the APPLET memory pool by 24 MiB by default, giving this memory to the SYSTEM pool. This allows custom sysmodules to use more memory without hitting the SYSTEM memory limit.
diff --git a/docs/modules/sm.md b/docs/modules/sm.md
new file mode 100644
index 000000000..8852975f8
--- /dev/null
+++ b/docs/modules/sm.md
@@ -0,0 +1,69 @@
+# sm
+
+sm is a reimplementation of Nintendo's service manager. It allows Atmosphère to add or remove process handle limits, add new services, or intercept service calls. This allows high-level intercepting of Horizon OS functionality.
+
+## Atmosphère Extensions
+
+There are a few ways in which the Stratosphere implementation of sm differs intentionally from the stock sm.
+
+### IPC: MITM Commands
+
+The Stratosphere implementation of sm adds a few additional commands to the [`sm:`](https://reswitched.github.io/SwIPC/ifaces.html#nn::sm::detail::IUserInterface) port session.
+
+Their SwIPC definitions follow.
+```
+interface nn::sm::detail::IUserInterface is sm: {
+ ...
+ [65000] AtmosphereInstallMitm(ServiceName service) -> handle service, handle query;
+ [65001] AtmosphereUninstallMitm(ServiceName service);
+ [65002] AtmosphereAssociatePidTidForMitm(u64 pid, u64 tid);
+}
+```
+
+#### AtmosphereInstallMitm
+
+This command alters the registration for the named service, in order to allow services to intercept communication between client processes and their intended services. It is used by [fs_mitm](fs_mitm.md).
+
+It takes the name of the service to install an MITM for, and returns two handles. The first is a port handle, similar to those returned from the [RegisterService](https://reswitched.github.io/SwIPC/ifaces.html#nn::sm::detail::IUserInterface(2)) command. The second is the server side of a session, called the query session. This session will used by sm to determine whether or not a new session should be intercepted, and to inform the MITM service of the identity of new processes.
+
+The query session is expected to implement the following interface.
+```
+interface MitmQueryService {
+ [65000] ShouldMitm(u64 pid) -> u64 should_mitm;
+ [65001] AssociatePidTid(u64 pid, u64 tid);
+}
+```
+
+The `ShouldMitm` command is invoked whenever a process attempts to make a new connection to the MITM'd service. It should return `0` if the process's connection should not be intercepted. Any other value will cause the process's connection to be intercepted. If the command returns an error code, the process's connection will not be intercepted.
+
+The `AssociatePidTid` command is invoked on all MITM query sessions whenever a new process is created, in order to inform those services of the identity of a newly created process before it attempts to connect to any services.
+
+If the process that installed the MITM attempts to connect to the service, it will always connect to the original service.
+
+This command requires that the session be initialized, returning error code 0x415 if it is not.
+If the given service name is invalid, error code 0xC15 is returned.
+If the user does not have service registration permission for the named service, error code 0x1015 is returned.
+If the service has not yet been registered, error code 0xE15 is returned.
+If the service already has an MITM installed, error code 0x815 is returned.
+
+#### AtmosphereUninstallMitm
+
+Removes any installed MITM for the named service.
+
+This command requires that the session be initialized, returning error code 0x415 if it is not.
+
+#### AtmosphereAssociatePidTidForMitm
+
+This command is used internally by the Stratosphere implementation of the [loader](loader.md) sysmodule, when a new process is created. It will call the `AssociatePidTid` command on every registered MITM query session.
+
+If the given process ID refers to a kernel internal process, error code 0x1015 is returned. This command requires that the session be initialized, returning error code 0x415 if it is not.
+
+### Minimum Session Limit
+
+When a service is registered, the sysmodule registering it must specify a limit on the number of sessions that are allowed to be active for that service at a time. This is used to ensure that services like `fs-pr`, `fs-ldr`, and `ldr:pm` can only be connected to once, adding an additional layer of safety over the regular service verification to ensure that those services are only connected to by the highly priveleged process they are intended to be used by.
+
+By default, the Stratosphere implementation of PM will raise any session limits to at least 8, meaning that for services like `fs-pr` and those mentioned above, up to 8 processes will be able to connect to those sessions, leaving 7 sessions for homebrew to use.
+
+### Weak Service Verification
+
+In system firmware versions before 3.0.1, if a process did not call the [Initialize](https://reswitched.github.io/SwIPC/ifaces.html#nn::sm::detail::IUserInterface(0)) command on its `sm:` session, normally used to inform sm of the process's identity, sm would assume that the process was a kernel internal process and skip any service registration or access checks. The Stratosphere implementation of sm reimplements this vulnerability, allowing homebrew processes to skip service registration and access checks.
diff --git a/docs/roadmap.md b/docs/roadmap.md
new file mode 100644
index 000000000..03367dff9
--- /dev/null
+++ b/docs/roadmap.md
@@ -0,0 +1,16 @@
+# Planned Features
+The following features are planned to be added in future versions of Atmosphère:
++ Thermosphère, a hypervisor-based emunand implementation.
++ A feature-rich debugging toolset (a component of Stratosphère).
+ + A custom debug monitor system module, providing an API for debugging Switch's processes. This may not be a reimplementation of Nintendo's own debug monitor.
+ + This should include a gdbstub implementation, possibly borrowing from Luma3DS's.
+ + This API should be additionally usable for RAM Editing/"Cheat Engine" purposes.
+ + A custom shell system module, providing an means for users to perform various RPC (with support for common/interesting functionality) on their Switch remotely. This may not be a reimplementation of Nintendo's own shell.
+ + This should support client connections over both Wi-Fi and USB.
+ + A custom logging system module, providing a means for other Atmosphère components (and possibly Nintendo's own system modules) to log debug output.
+ + This should support logging to the SD card, over Wi-Fi, and over USB.
++ An application-level plugin system.
+ + This will, ideally, work somewhat like NTR-CFW's plugin system on the 3DS, allowing users to run their own code in a game's process in their own thread.
++ An AR Code/Gameshark analog implementation, allowing for easy sharing/development of cheat codes to run on device.
++ Further extensions to existing Atmosphère components.
++ General system stability improvements to enhance the user's experience.
diff --git a/exosphere/Makefile b/exosphere/Makefile
index 77e3e02da..efadefc58 100644
--- a/exosphere/Makefile
+++ b/exosphere/Makefile
@@ -20,7 +20,7 @@ TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := src src/dbg
DATA := data
-INCLUDES := include
+INCLUDES := include ../common/include
#---------------------------------------------------------------------------------
# options for code generation
diff --git a/exosphere/src/configitem.c b/exosphere/src/configitem.c
index 577f74502..389bbcaec 100644
--- a/exosphere/src/configitem.c
+++ b/exosphere/src/configitem.c
@@ -15,6 +15,7 @@
*/
#include
+#include
#include "bootconfig.h"
#include "configitem.h"
@@ -25,7 +26,6 @@
#include "utils.h"
#include "masterkey.h"
#include "exocfg.h"
-#include "version.h"
static bool g_battery_profile = false;
@@ -151,7 +151,11 @@ uint32_t configitem_get(ConfigItem item, uint64_t *p_outvalue) {
break;
case CONFIGITEM_EXOSPHERE_VERSION:
/* UNOFFICIAL: Gets information about the current exosphere version. */
- *p_outvalue = (EXOSPHERE_RELEASE_VERSION_MAJOR << 24) | (EXOSPHERE_RELEASE_VERSION_MINOR << 16) | (exosphere_get_target_firmware() << 8) | (mkey_get_revision() << 0);
+ *p_outvalue = ((uint64_t)(ATMOSPHERE_RELEASE_VERSION_MAJOR & 0xFF) << 32ull) |
+ ((uint64_t)(ATMOSPHERE_RELEASE_VERSION_MINOR & 0xFF) << 24ull) |
+ ((uint64_t)(ATMOSPHERE_RELEASE_VERSION_MICRO & 0xFF) << 16ull) |
+ ((uint64_t)(exosphere_get_target_firmware() & 0xFF) << 8ull) |
+ ((uint64_t)(mkey_get_revision() & 0xFF) << 0ull);
break;
default:
result = 2;
diff --git a/exosphere/src/package2.c b/exosphere/src/package2.c
index 6e1620bc1..1281f9498 100644
--- a/exosphere/src/package2.c
+++ b/exosphere/src/package2.c
@@ -304,7 +304,7 @@ static bool validate_package2_metadata(package2_meta_t *metadata) {
/* Perform version checks. */
/* We will be compatible with all package2s released before current, but not newer ones. */
- if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_500_CURRENT) {
+ if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_600_CURRENT) {
return true;
}
@@ -331,7 +331,7 @@ static uint32_t decrypt_and_validate_header(package2_header_t *header) {
}
/* Ensure we successfully decrypted the header. */
- if (mkey_rev > mkey_get_revision()) {
+ if (mkey_rev > mkey_get_revision()) {
panic(0xFAF00003);
}
} else if (!validate_package2_metadata(&header->metadata)) {
@@ -478,7 +478,7 @@ void load_package2(coldboot_crt0_reloc_list_t *reloc_list) {
MAKE_REG32(PMC_BASE + 0x334) |= 0x10;
switch (exosphere_get_target_firmware()) {
case EXOSPHERE_TARGET_FIRMWARE_400:
- MAKE_REG32(PMC_BASE + 0x360) = 5;
+ MAKE_REG32(PMC_BASE + 0x360) = 0x105;
break;
case EXOSPHERE_TARGET_FIRMWARE_500:
MAKE_REG32(PMC_BASE + 0x360) = 6;
diff --git a/exosphere/src/package2.h b/exosphere/src/package2.h
index ec0dfba3e..9553fa8fe 100644
--- a/exosphere/src/package2.h
+++ b/exosphere/src/package2.h
@@ -66,14 +66,16 @@ static inline uintptr_t get_nx_bootloader_mailbox_base(void) {
#define PACKAGE2_MAXVER_300 0x4
#define PACKAGE2_MAXVER_302 0x5
#define PACKAGE2_MAXVER_400_410 0x6
-#define PACKAGE2_MAXVER_500_CURRENT 0x7
+#define PACKAGE2_MAXVER_500_510 0x7
+#define PACKAGE2_MAXVER_600_CURRENT 0x8
#define PACKAGE2_MINVER_100 0x3
#define PACKAGE2_MINVER_200 0x4
#define PACKAGE2_MINVER_300 0x5
#define PACKAGE2_MINVER_302 0x6
#define PACKAGE2_MINVER_400_410 0x7
-#define PACKAGE2_MINVER_500_CURRENT 0x8
+#define PACKAGE2_MINVER_500_510 0x8
+#define PACKAGE2_MINVER_600_CURRENT 0x9
typedef struct {
union {
diff --git a/fusee/fusee-primary/Makefile b/fusee/fusee-primary/Makefile
index 34307fcbe..8dce2efff 100644
--- a/fusee/fusee-primary/Makefile
+++ b/fusee/fusee-primary/Makefile
@@ -20,7 +20,7 @@ TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := src src/sdmmc src/lib src/lib/fatfs src/display
DATA := data
-INCLUDES := include
+INCLUDES := include ../../common/include
#---------------------------------------------------------------------------------
# options for code generation
diff --git a/fusee/fusee-secondary/Makefile b/fusee/fusee-secondary/Makefile
index 55377889f..9055bbf59 100644
--- a/fusee/fusee-secondary/Makefile
+++ b/fusee/fusee-secondary/Makefile
@@ -23,7 +23,7 @@ TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := src src/sdmmc src/lib src/lib/fatfs src/display
DATA := data
-INCLUDES := include
+INCLUDES := include ../../common/include
#---------------------------------------------------------------------------------
# options for code generation
@@ -69,7 +69,7 @@ ifneq ($(BUILD),$(notdir $(CURDIR)))
export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
-export KIPDIRS := $(AMS)/stratosphere/loader $(AMS)/stratosphere/pm $(AMS)/stratosphere/sm $(AMS)/stratosphere/boot
+export KIPDIRS := $(AMS)/stratosphere/loader $(AMS)/stratosphere/pm $(AMS)/stratosphere/sm $(AMS)/stratosphere/boot $(AMS)/stratosphere/fs_mitm
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
$(AMS)/exosphere $(AMS)/thermosphere $(KIPDIRS)
@@ -79,7 +79,7 @@ export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
-KIPFILES := loader.kip pm.kip sm.kip boot_100.kip boot_200.kip
+KIPFILES := loader.kip pm.kip sm.kip fs_mitm.kip boot_100.kip boot_200.kip
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) exosphere.bin thermosphere.bin splash_screen.bmp $(KIPFILES)
#---------------------------------------------------------------------------------
diff --git a/fusee/fusee-secondary/src/ips.c b/fusee/fusee-secondary/src/ips.c
new file mode 100644
index 000000000..41c2840a6
--- /dev/null
+++ b/fusee/fusee-secondary/src/ips.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 2018 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "utils.h"
+#include "se.h"
+#include "lib/log.h"
+#include "ips.h"
+
+/* IPS Patching adapted from Luma3DS (https://github.com/AuroraWright/Luma3DS/blob/master/sysmodules/loader/source/patcher.c) */
+
+#define IPS_MAGIC "PATCH"
+#define IPS_TAIL "EOF"
+
+#define IPS32_MAGIC "IPS32"
+#define IPS32_TAIL "EEOF"
+
+/* Applies an IPS/IPS32 patch to memory, disregarding writes to the first prot_size bytes. */
+static void apply_ips_patch(uint8_t *mem, size_t mem_size, size_t prot_size, bool is_ips32, FILE *f_ips) {
+ uint8_t buffer[4];
+ while (fread(buffer, is_ips32 ? 4 : 3, 1, f_ips) == 1) {
+ if (is_ips32 && memcmp(buffer, IPS32_TAIL, 4) == 0) {
+ break;
+ } else if (!is_ips32 && memcmp(buffer, IPS_TAIL, 3) == 0) {
+ break;
+ }
+
+ /* Offset of patch. */
+ uint32_t patch_offset;
+ if (is_ips32) {
+ patch_offset = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
+ } else {
+ patch_offset = (buffer[0] << 16) | (buffer[1] << 8) | (buffer[2]);
+ }
+
+ /* Size of patch. */
+ if (fread(buffer, 2, 1, f_ips) != 1) {
+ break;
+ }
+ uint32_t patch_size = (buffer[0] << 8) | (buffer[1]);
+
+ /* Check for RLE encoding. */
+ if (patch_size == 0) {
+ /* Size of RLE. */
+ if (fread(buffer, 2, 1, f_ips) != 1) {
+ break;
+ }
+
+ uint32_t rle_size = (buffer[0] << 8) | (buffer[1]);
+
+ /* Value for RLE. */
+ if (fread(buffer, 1, 1, f_ips) != 1) {
+ break;
+ }
+
+ if (patch_offset < prot_size) {
+ if (patch_offset + rle_size > prot_size) {
+ uint32_t diff = prot_size - patch_offset;
+ patch_offset += diff;
+ rle_size -= diff;
+ goto IPS_RLE_PATCH_OFFSET_WITHIN_BOUNDS;
+ }
+ } else {
+ IPS_RLE_PATCH_OFFSET_WITHIN_BOUNDS:
+ if (patch_offset + rle_size > mem_size) {
+ rle_size = mem_size - patch_offset;
+ }
+ memset(mem + patch_offset, buffer[0], rle_size);
+ }
+ } else {
+ uint32_t read_size;
+ if (patch_offset < prot_size) {
+ if (patch_offset + patch_size > prot_size) {
+ uint32_t diff = prot_size - patch_offset;
+ patch_offset += diff;
+ patch_size -= diff;
+ fseek(f_ips, diff, SEEK_CUR);
+ goto IPS_DATA_PATCH_OFFSET_WITHIN_BOUNDS;
+ } else {
+ fseek(f_ips, patch_size, SEEK_CUR);
+ }
+ } else {
+ IPS_DATA_PATCH_OFFSET_WITHIN_BOUNDS:
+ read_size = patch_size;
+ if (patch_offset + read_size > mem_size) {
+ read_size = mem_size - patch_offset;
+ }
+ if (fread(mem + patch_offset, read_size, 1, f_ips) != 1) {
+ break;
+ }
+ if (patch_size > read_size) {
+ fseek(f_ips, patch_size - read_size, SEEK_CUR);
+ }
+ }
+ }
+ }
+}
+
+
+static inline uint8_t hex_nybble_to_u8(const char nybble) {
+ if ('0' <= nybble && nybble <= '9') {
+ return nybble - '0';
+ } else if ('a' <= nybble && nybble <= 'f') {
+ return nybble - 'a' + 0xa;
+ } else {
+ return nybble - 'A' + 0xA;
+ }
+}
+
+static bool name_matches_hash(const char *name, size_t name_len, const void *hash, size_t hash_size) {
+ /* Validate name is hex build id. */
+ for (unsigned int i = 0; i < name_len - 4; i++) {
+ if (isxdigit(name[i]) == 0) {
+ return false;
+ }
+ }
+
+ /* Read hash from name. */
+ uint8_t hash_from_name[0x20] = {0};
+ for (unsigned int name_ofs = 0, id_ofs = 0; name_ofs < name_len - 4; id_ofs++) {
+ hash_from_name[id_ofs] |= hex_nybble_to_u8(name[name_ofs++]) << 4;
+ hash_from_name[id_ofs] |= hex_nybble_to_u8(name[name_ofs++]);
+ }
+
+ return memcmp(hash, hash_from_name, hash_size) == 0;
+
+}
+
+static bool has_ips_patches(const char *dir, const void *hash, size_t hash_size) {
+ bool any_patches = false;
+ char path[0x301] = {0};
+ snprintf(path, sizeof(path) - 1, "%s", dir);
+ DIR *patches_dir = opendir(path);
+ struct dirent *pdir_ent;
+ if (patches_dir != NULL) {
+ /* Iterate over the patches directory to find patch subdirectories. */
+ while ((pdir_ent = readdir(patches_dir)) != NULL && !any_patches) {
+ if (strcmp(pdir_ent->d_name, ".") == 0 || strcmp(pdir_ent->d_name, "..") == 0) {
+ continue;
+ }
+ snprintf(path, sizeof(path) - 1, "%s/%s", dir, pdir_ent->d_name);
+ DIR *patch_dir = opendir(path);
+ struct dirent *ent;
+ if (patch_dir != NULL) {
+ /* Iterate over the patch subdirectory to find .ips patches. */
+ while ((ent = readdir(patch_dir)) != NULL && !any_patches) {
+ if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
+ continue;
+ }
+ size_t name_len = strlen(ent->d_name);
+ if ((4 < name_len && name_len <= 0x44) && ((name_len & 1) == 0) && strcmp(ent->d_name + name_len - 4, ".ips") == 0 && name_matches_hash(ent->d_name, name_len, hash, hash_size)) {
+ snprintf(path, sizeof(path) - 1, "%s/%s/%s", dir, pdir_ent->d_name, ent->d_name);
+ FILE *f_ips = fopen(path, "rb");
+ if (f_ips != NULL) {
+ uint8_t header[5];
+ if (fread(header, 5, 1, f_ips) == 1) {
+ if (memcmp(header, IPS_MAGIC, 5) == 0 || memcmp(header, IPS32_MAGIC, 5) == 0) {
+ any_patches = true;
+ }
+ }
+ fclose(f_ips);
+ }
+ }
+ }
+ closedir(patch_dir);
+ }
+ }
+ closedir(patches_dir);
+ }
+
+ return any_patches;
+}
+
+static void apply_ips_patches(const char *dir, void *mem, size_t mem_size, size_t prot_size, const void *hash, size_t hash_size) {
+ char path[0x301] = {0};
+ snprintf(path, sizeof(path) - 1, "%s", dir);
+ DIR *patches_dir = opendir(path);
+ struct dirent *pdir_ent;
+ if (patches_dir != NULL) {
+ /* Iterate over the patches directory to find patch subdirectories. */
+ while ((pdir_ent = readdir(patches_dir)) != NULL) {
+ if (strcmp(pdir_ent->d_name, ".") == 0 || strcmp(pdir_ent->d_name, "..") == 0) {
+ continue;
+ }
+ snprintf(path, sizeof(path) - 1, "%s/%s", dir, pdir_ent->d_name);
+ DIR *patch_dir = opendir(path);
+ struct dirent *ent;
+ if (patch_dir != NULL) {
+ /* Iterate over the patch subdirectory to find .ips patches. */
+ while ((ent = readdir(patch_dir)) != NULL) {
+ if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
+ continue;
+ }
+ size_t name_len = strlen(ent->d_name);
+ if ((4 < name_len && name_len <= 0x44) && ((name_len & 1) == 0) && strcmp(ent->d_name + name_len - 4, ".ips") == 0 && name_matches_hash(ent->d_name, name_len, hash, hash_size)) {
+ snprintf(path, sizeof(path) - 1, "%s/%s/%s", dir, pdir_ent->d_name, ent->d_name);
+ FILE *f_ips = fopen(path, "rb");
+ if (f_ips != NULL) {
+ uint8_t header[5];
+ if (fread(header, 5, 1, f_ips) == 1) {
+ if (memcmp(header, IPS_MAGIC, 5) == 0) {
+ apply_ips_patch(mem, mem_size, prot_size, false, f_ips);
+ } else if (memcmp(header, IPS32_MAGIC, 5) == 0) {
+ apply_ips_patch(mem, mem_size, prot_size, true, f_ips);
+ }
+ }
+ fclose(f_ips);
+ }
+ }
+ }
+ closedir(patch_dir);
+ }
+ }
+ closedir(patches_dir);
+ }
+}
+
+void apply_kernel_ips_patches(void *kernel, size_t kernel_size) {
+ uint8_t hash[0x20];
+ se_calculate_sha256(hash, kernel, kernel_size);
+ apply_ips_patches("atmosphere/kernel_patches", kernel, kernel_size, 0, hash, sizeof(hash));
+}
+
+static void kip1_blz_uncompress(void *hdr_end) {
+ uint8_t *u8_hdr_end = (uint8_t *)hdr_end;
+ uint32_t addl_size = ((u8_hdr_end[-4]) << 0) | ((u8_hdr_end[-3]) << 8) | ((u8_hdr_end[-2]) << 16) | ((u8_hdr_end[-1]) << 24);
+ uint32_t header_size = ((u8_hdr_end[-8]) << 0) | ((u8_hdr_end[-7]) << 8) | ((u8_hdr_end[-6]) << 16) | ((u8_hdr_end[-5]) << 24);
+ uint32_t cmp_and_hdr_size = ((u8_hdr_end[-12]) << 0) | ((u8_hdr_end[-11]) << 8) | ((u8_hdr_end[-10]) << 16) | ((u8_hdr_end[-9]) << 24);
+
+ unsigned char *cmp_start = (unsigned char *)(((uintptr_t)hdr_end) - cmp_and_hdr_size);
+ uint32_t cmp_ofs = cmp_and_hdr_size - header_size;
+ uint32_t out_ofs = cmp_and_hdr_size + addl_size;
+
+ while (out_ofs) {
+ unsigned char control = cmp_start[--cmp_ofs];
+ for (unsigned int i = 0; i < 8; i++) {
+ if (control & 0x80) {
+ if (cmp_ofs < 2) {
+ fatal_error("KIP1 decompression out of bounds!\n");
+ }
+ cmp_ofs -= 2;
+ uint16_t seg_val = ((unsigned int)cmp_start[cmp_ofs+1] << 8) | cmp_start[cmp_ofs];
+ uint32_t seg_size = ((seg_val >> 12) & 0xF) + 3;
+ uint32_t seg_ofs = (seg_val & 0x0FFF) + 3;
+ if (out_ofs < seg_size) {
+ /* Kernel restricts segment copy to stay in bounds. */
+ seg_size = out_ofs;
+ }
+ out_ofs -= seg_size;
+
+ for (unsigned int j = 0; j < seg_size; j++) {
+ cmp_start[out_ofs + j] = cmp_start[out_ofs + j + seg_ofs];
+ }
+ } else {
+ /* Copy directly. */
+ if (cmp_ofs < 1) {
+ fatal_error("KIP1 decompression out of bounds!\n");
+ }
+ cmp_start[--out_ofs] = cmp_start[--cmp_ofs];
+ }
+ control <<= 1;
+ if (out_ofs == 0) {
+ return;
+ }
+ }
+ }
+}
+
+static kip1_header_t *kip1_uncompress(kip1_header_t *kip, size_t *size) {
+ kip1_header_t new_header = *kip;
+ for (unsigned int i = 0; i < 3; i++) {
+ new_header.section_headers[i].compressed_size = new_header.section_headers[i].out_size;
+ }
+ new_header.flags &= 0xF8;
+
+ *size = kip1_get_size_from_header(&new_header);
+
+ unsigned char *new_kip = calloc(1, *size);
+ if (new_kip == NULL) {
+ return NULL;
+ }
+ *((kip1_header_t *)new_kip) = new_header;
+
+ size_t new_offset = 0x100;
+ size_t old_offset = 0x100;
+ for (unsigned int i = 0; i < 3; i++) {
+ memcpy(new_kip + new_offset, (unsigned char *)kip + old_offset, kip->section_headers[i].compressed_size);
+ if (kip->flags & (1 << i)) {
+ kip1_blz_uncompress(new_kip + new_offset + kip->section_headers[i].compressed_size);
+ }
+ new_offset += kip->section_headers[i].out_size;
+ old_offset += kip->section_headers[i].compressed_size;
+ }
+
+ return (kip1_header_t *)new_kip;
+}
+
+kip1_header_t *apply_kip_ips_patches(kip1_header_t *kip, size_t kip_size) {
+ uint8_t hash[0x20];
+ se_calculate_sha256(hash, kip, kip_size);
+
+ if (!has_ips_patches("atmosphere/kip_patches", hash, sizeof(hash))) {
+ return NULL;
+ }
+
+ print(SCREEN_LOG_LEVEL_MANDATORY, "[NXBOOT]: Patching KIP %08x%08x...\n", (uint32_t)(kip->title_id >> 32), (uint32_t)kip->title_id);
+
+
+ size_t uncompressed_kip_size;
+ kip1_header_t *uncompressed_kip = kip1_uncompress(kip, &uncompressed_kip_size);
+ if (uncompressed_kip == NULL) {
+ return NULL;
+ }
+
+ apply_ips_patches("atmosphere/kip_patches", uncompressed_kip, uncompressed_kip_size, 0x100, hash, sizeof(hash));
+ return uncompressed_kip;
+}
diff --git a/fusee/fusee-secondary/src/ips.h b/fusee/fusee-secondary/src/ips.h
new file mode 100644
index 000000000..6db00a1f1
--- /dev/null
+++ b/fusee/fusee-secondary/src/ips.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2018 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef FUSEE_IPS_H
+#define FUSEE_IPS_H
+
+#include "utils.h"
+#include "kip.h"
+#include
+
+void apply_kernel_ips_patches(void *kernel, size_t kernel_size);
+kip1_header_t *apply_kip_ips_patches(kip1_header_t *kip, size_t kip_size);
+
+#endif
diff --git a/fusee/fusee-secondary/src/kernel_patches.c b/fusee/fusee-secondary/src/kernel_patches.c
index f0741350a..909a066aa 100644
--- a/fusee/fusee-secondary/src/kernel_patches.c
+++ b/fusee/fusee-secondary/src/kernel_patches.c
@@ -18,6 +18,7 @@
#include "utils.h"
#include "se.h"
#include "kernel_patches.h"
+#include "ips.h"
#define MAKE_BRANCH(a, o) 0x14000000 | ((((o) - (a)) >> 2) & 0x3FFFFFF)
@@ -510,9 +511,13 @@ const kernel_info_t *get_kernel_info(void *kernel, size_t size) {
return NULL;
}
-void package2_patch_kernel(void *_kernel, size_t size) {
+void package2_patch_kernel(void *_kernel, size_t size, bool is_sd_kernel) {
const kernel_info_t *kernel_info = get_kernel_info(_kernel, size);
- if (kernel_info == NULL) {
+
+ /* Apply IPS patches. */
+ apply_kernel_ips_patches(_kernel, size);
+
+ if (kernel_info == NULL && !is_sd_kernel) {
/* Should this be fatal? */
fatal_error("kernel_patcher: unable to identify kernel!\n");
}
diff --git a/fusee/fusee-secondary/src/kernel_patches.h b/fusee/fusee-secondary/src/kernel_patches.h
index 7dc2c7795..1dfb21fbf 100644
--- a/fusee/fusee-secondary/src/kernel_patches.h
+++ b/fusee/fusee-secondary/src/kernel_patches.h
@@ -19,6 +19,6 @@
#include "utils.h"
-void package2_patch_kernel(void *kernel, size_t kernel_size);
+void package2_patch_kernel(void *kernel, size_t kernel_size, bool is_sd_kernel);
#endif
\ No newline at end of file
diff --git a/fusee/fusee-secondary/src/main.c b/fusee/fusee-secondary/src/main.c
index d10e6e7cb..753efc0aa 100644
--- a/fusee/fusee-secondary/src/main.c
+++ b/fusee/fusee-secondary/src/main.c
@@ -101,13 +101,13 @@ int main(int argc, void **argv) {
g_do_nxboot = loader_ctx->chainload_entrypoint == 0;
if (g_do_nxboot) {
- print(SCREEN_LOG_LEVEL_INFO, "Now performing nxboot.\n");
+ print(SCREEN_LOG_LEVEL_MANDATORY, "Now performing nxboot.\n");
uint32_t boot_memaddr = nxboot_main();
nxboot_finish(boot_memaddr);
} else {
/* TODO: What else do we want to do in terms of argc/argv? */
const char *path = get_loader_ctx()->file_paths_to_load[get_loader_ctx()->file_id_of_entrypoint];
- print(SCREEN_LOG_LEVEL_INFO, "Now chainloading.\n");
+ print(SCREEN_LOG_LEVEL_MANDATORY, "Now chainloading.\n");
g_chainloader_argc = 1;
strcpy(g_chainloader_arg_data, path);
}
diff --git a/fusee/fusee-secondary/src/nxboot.c b/fusee/fusee-secondary/src/nxboot.c
index 83fb6ca3a..9b9c73ed2 100644
--- a/fusee/fusee-secondary/src/nxboot.c
+++ b/fusee/fusee-secondary/src/nxboot.c
@@ -292,6 +292,8 @@ uint32_t nxboot_main(void) {
fatal_error("[NXBOOT]: Failed to detect target firmware!\n");
else
print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT]: Detected target firmware %ld!\n", target_firmware);
+
+ print(SCREEN_LOG_LEVEL_MANDATORY, "[NXBOOT]: Loaded firmware from eMMC...\n");
/* Setup boot configuration for Exosphère. */
nxboot_configure_exosphere(target_firmware);
@@ -357,7 +359,7 @@ uint32_t nxboot_main(void) {
pmc->scratch1 = (uint32_t)warmboot_memaddr;
}
- print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT]: Rebuilding package2...\n");
+ print(SCREEN_LOG_LEVEL_MANDATORY, "[NXBOOT]: Rebuilding package2...\n");
/* Patch package2, adding Thermosphère + custom KIPs. */
package2_rebuild_and_copy(package2, MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware);
diff --git a/fusee/fusee-secondary/src/package2.c b/fusee/fusee-secondary/src/package2.c
index 50eada5f8..1d2fb3a5c 100644
--- a/fusee/fusee-secondary/src/package2.c
+++ b/fusee/fusee-secondary/src/package2.c
@@ -23,6 +23,7 @@
#include "kernel_patches.h"
#include "kip.h"
#include "se.h"
+#include "fs_utils.h"
#define u8 uint8_t
#define u32 uint32_t
@@ -47,6 +48,7 @@ void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firm
size_t rebuilt_package2_size;
void *kernel;
size_t kernel_size;
+ bool is_sd_kernel = false;
void *thermosphere;
size_t thermosphere_size;
ini1_header_t *orig_ini1, *rebuilt_ini1;
@@ -63,9 +65,28 @@ void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firm
if (thermosphere_size != 0 && package2->metadata.section_sizes[PACKAGE2_SECTION_UNUSED] != 0) {
fatal_error(u8"Error: Package2 has no unused section for Thermosphère!\n");
}
+
+ /* Load Kernel from SD, if possible. */
+ {
+ size_t sd_kernel_size = get_file_size("atmosphere/kernel.bin");
+ if (sd_kernel_size != 0) {
+ if (sd_kernel_size > PACKAGE2_SIZE_MAX) {
+ fatal_error("Error: atmosphere/kernel.bin is too large!\n");
+ }
+ kernel = malloc(sd_kernel_size);
+ if (kernel == NULL) {
+ fatal_error("Error: failed to allocate kernel!\n");
+ }
+ if (read_from_file(kernel, sd_kernel_size, "atmosphere/kernel.bin") != sd_kernel_size) {
+ fatal_error("Error: failed to read atmosphere/kernel.bin!\n");
+ }
+ kernel_size = sd_kernel_size;
+ is_sd_kernel = true;
+ }
+ }
/* Perform any patches we want to the NX kernel. */
- package2_patch_kernel(kernel, kernel_size);
+ package2_patch_kernel(kernel, kernel_size, is_sd_kernel);
print(SCREEN_LOG_LEVEL_DEBUG, "Rebuilding the INI1 section...\n");
package2_get_src_section((void *)&orig_ini1, package2, PACKAGE2_SECTION_INI1);
@@ -283,6 +304,7 @@ static ini1_header_t *package2_rebuild_ini1(ini1_header_t *ini1, uint32_t target
ini1_header_t *inis_to_merge[STRATOSPHERE_INI1_MAX] = {0};
ini1_header_t *merged;
+ inis_to_merge[STRATOSPHERE_INI1_SDFILES] = stratosphere_get_sd_files_ini1();
inis_to_merge[STRATOSPHERE_INI1_EMBEDDED] = stratosphere_get_ini1(target_firmware);
inis_to_merge[STRATOSPHERE_INI1_PACKAGE2] = ini1;
diff --git a/fusee/fusee-secondary/src/stratosphere.c b/fusee/fusee-secondary/src/stratosphere.c
index 22d058b92..2a07e1ff0 100644
--- a/fusee/fusee-secondary/src/stratosphere.c
+++ b/fusee/fusee-secondary/src/stratosphere.c
@@ -17,11 +17,14 @@
#include
#include
#include
+#include
#include "exocfg.h"
#include "utils.h"
#include "package2.h"
#include "stratosphere.h"
#include "fs_utils.h"
+#include "ips.h"
+#include "lib/log.h"
#define u8 uint8_t
#define u32 uint32_t
@@ -34,16 +37,18 @@
#undef u32
static ini1_header_t *g_stratosphere_ini1 = NULL;
+static ini1_header_t *g_sd_files_ini1 = NULL;
static bool g_stratosphere_loader_enabled = true;
static bool g_stratosphere_sm_enabled = true;
static bool g_stratosphere_pm_enabled = true;
+static bool g_stratosphere_fs_mitm_enabled = true;
static bool g_stratosphere_boot_enabled = false;
extern const uint8_t boot_100_kip[], boot_200_kip[];
-extern const uint8_t loader_kip[], pm_kip[], sm_kip[];
+extern const uint8_t loader_kip[], pm_kip[], sm_kip[], fs_mitm_kip[];
extern const uint32_t boot_100_kip_size, boot_200_kip_size;
-extern const uint32_t loader_kip_size, pm_kip_size, sm_kip_size;
+extern const uint32_t loader_kip_size, pm_kip_size, sm_kip_size, fs_mitm_kip_size;
/* GCC doesn't consider the size as const... we have to write it ourselves. */
@@ -83,6 +88,11 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
num_processes++;
}
+ if (g_stratosphere_fs_mitm_enabled) {
+ size += fs_mitm_kip_size;
+ num_processes++;
+ }
+
if (g_stratosphere_boot_enabled) {
size += boot_kip_size;
num_processes++;
@@ -117,6 +127,11 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
data += sm_kip_size;
}
+ if (g_stratosphere_fs_mitm_enabled) {
+ memcpy(data, fs_mitm_kip, fs_mitm_kip_size);
+ data += fs_mitm_kip_size;
+ }
+
if (g_stratosphere_boot_enabled) {
memcpy(data, boot_kip, boot_kip_size);
data += boot_kip_size;
@@ -126,13 +141,111 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
}
void stratosphere_free_ini1(void) {
- free(g_stratosphere_ini1);
- g_stratosphere_ini1 = NULL;
+ if (g_stratosphere_ini1 != NULL) {
+ free(g_stratosphere_ini1);
+ g_stratosphere_ini1 = NULL;
+ }
+ if (g_sd_files_ini1 != NULL) {
+ free(g_sd_files_ini1);
+ g_sd_files_ini1 = NULL;
+ }
+}
+
+
+
+static void try_add_sd_kip(ini1_header_t *ini1, const char *kip_path) {
+ size_t file_size = get_file_size(kip_path);
+
+ if (ini1->size + file_size > PACKAGE2_SIZE_MAX) {
+ fatal_error("Failed to load %s: INI1 would be too large!\n", kip_path);
+ }
+
+ kip1_header_t kip_header;
+ if (read_from_file(&kip_header, sizeof(kip_header), kip_path) != sizeof(kip_header) || kip_header.magic != MAGIC_KIP1) {
+ return;
+ }
+
+ size_t kip_size = kip1_get_size_from_header(&kip_header);
+ if (kip_size > file_size) {
+ fatal_error("Failed to load %s: KIP size is corrupt!\n", kip_path);
+ }
+
+ if (read_from_file(ini1->kip_data + ini1->size - sizeof(ini1_header_t), kip_size, kip_path) != kip_size) {
+ /* TODO: is this error fatal? */
+ return;
+ }
+
+ ini1->size += kip_size;
+ ini1->num_processes++;
+}
+
+ini1_header_t *stratosphere_get_sd_files_ini1(void) {
+ if (g_sd_files_ini1 != NULL) {
+ return g_sd_files_ini1;
+ }
+
+ /* Allocate space. */
+ g_sd_files_ini1 = (ini1_header_t *)malloc(PACKAGE2_SIZE_MAX);
+ g_sd_files_ini1->magic = MAGIC_INI1;
+ g_sd_files_ini1->size = sizeof(ini1_header_t);
+ g_sd_files_ini1->num_processes = 0;
+ g_sd_files_ini1->_0xC = 0;
+
+ /* Load all kips from /atmosphere/kips/<*>.kip or /atmosphere/kips/<*>/<*>.kip. */
+ DIR *kips_dir = opendir("atmosphere/kips");
+ struct dirent *ent;
+ if (kips_dir != NULL) {
+ while ((ent = readdir(kips_dir)) != NULL) {
+ if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
+ continue;
+ }
+
+ char kip_path[0x301] = {0};
+ snprintf(kip_path, 0x300, "atmosphere/kips/%s", ent->d_name);
+
+ struct stat kip_stat;
+ if (stat(kip_path, &kip_stat) == -1) {
+ continue;
+ }
+
+ if ((kip_stat.st_mode & S_IFMT) == S_IFREG) {
+ /* If file, add to ini1. */
+ try_add_sd_kip(g_sd_files_ini1, kip_path);
+ } else if ((kip_stat.st_mode & S_IFMT) == S_IFDIR) {
+ /* Otherwise, allow one level of nesting. */
+ DIR *sub_dir = opendir(kip_path);
+ struct dirent *sub_ent;
+ if (sub_dir != NULL) {
+ while ((sub_ent = readdir(sub_dir)) != NULL) {
+ if (strcmp(sub_ent->d_name, ".") == 0 || strcmp(sub_ent->d_name, "..") == 0) {
+ continue;
+ }
+
+ /* Reuse kip path variable. */
+ memset(kip_path, 0, sizeof(kip_path));
+ snprintf(kip_path, 0x300, "atmosphere/kips/%s/%s", ent->d_name, sub_ent->d_name);
+
+ if (stat(kip_path, &kip_stat) == -1) {
+ continue;
+ }
+
+ if ((kip_stat.st_mode & S_IFMT) == S_IFREG) {
+ /* If file, add to ini1. */
+ try_add_sd_kip(g_sd_files_ini1, kip_path);
+ }
+ }
+ closedir(sub_dir);
+ }
+ }
+ }
+ closedir(kips_dir);
+ }
+
+ return g_sd_files_ini1;
}
/* Merges some number of INI1s into a single INI1. It's assumed that the INIs are in order of preference. */
ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis) {
- char sd_path[0x100] = {0};
uint32_t total_num_processes = 0;
/* Validate all ini headers. */
@@ -159,7 +272,6 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis) {
merged->num_processes = 0;
merged->_0xC = 0;
size_t remaining_size = PACKAGE2_SIZE_MAX - sizeof(ini1_header_t);
- size_t read_size;
unsigned char *current_dst_kip = merged->kip_data;
@@ -184,31 +296,26 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis) {
if (already_loaded) {
continue;
}
+
+ print(SCREEN_LOG_LEVEL_MANDATORY, "[NXBOOT]: Loading KIP %08x%08x...\n", (uint32_t)(current_kip->title_id >> 32), (uint32_t)current_kip->title_id);
- /* TODO: What folder should these be read out of? */
- snprintf(sd_path, sizeof(sd_path), "atmosphere/titles/%016llX/%016llX.kip", current_kip->title_id, current_kip->title_id);
-
- /* Try to load an override KIP from SD, if possible. */
- read_size = read_from_file(current_dst_kip, remaining_size, sd_path);
- if (read_size != 0) {
- kip1_header_t *sd_kip = (kip1_header_t *)(current_dst_kip);
- if (read_size < sizeof(kip1_header_t) || sd_kip->magic != MAGIC_KIP1) {
- fatal_error("%s is not a KIP1?\n", sd_path);
- } else if (sd_kip->title_id != current_kip->title_id) {
- fatal_error("%s has wrong Title ID!\n", sd_path);
- }
- size_t expected_sd_kip_size = kip1_get_size_from_header(sd_kip);
- if (expected_sd_kip_size != read_size) {
- fatal_error("%s has wrong size or there is not enough space (expected 0x%zx, read 0x%zx)!\n",
- sd_path, expected_sd_kip_size, read_size);
- }
- remaining_size -= expected_sd_kip_size;
- current_dst_kip += expected_sd_kip_size;
- } else {
- size_t current_kip_size = kip1_get_size_from_header(current_kip);
- if (current_kip_size > remaining_size) {
+ size_t current_kip_size = kip1_get_size_from_header(current_kip);
+ if (current_kip_size > remaining_size) {
+ fatal_error("Not enough space for all the KIP1s!\n");
+ }
+
+ kip1_header_t *patched_kip = apply_kip_ips_patches(current_kip, current_kip_size);
+ if (patched_kip != NULL) {
+ size_t patched_kip_size = kip1_get_size_from_header(patched_kip);
+ if (patched_kip_size > remaining_size) {
fatal_error("Not enough space for all the KIP1s!\n");
}
+ memcpy(current_dst_kip, patched_kip, patched_kip_size);
+ remaining_size -= patched_kip_size;
+ current_dst_kip += patched_kip_size;
+
+ free(patched_kip);
+ } else {
memcpy(current_dst_kip, current_kip, current_kip_size);
remaining_size -= current_kip_size;
current_dst_kip += current_kip_size;
diff --git a/fusee/fusee-secondary/src/stratosphere.h b/fusee/fusee-secondary/src/stratosphere.h
index 33915555b..2ae43f1be 100644
--- a/fusee/fusee-secondary/src/stratosphere.h
+++ b/fusee/fusee-secondary/src/stratosphere.h
@@ -20,11 +20,13 @@
#include "utils.h"
#include "kip.h"
-#define STRATOSPHERE_INI1_EMBEDDED 0x0
-#define STRATOSPHERE_INI1_PACKAGE2 0x1
-#define STRATOSPHERE_INI1_MAX 0x2
+#define STRATOSPHERE_INI1_SDFILES 0x0
+#define STRATOSPHERE_INI1_EMBEDDED 0x1
+#define STRATOSPHERE_INI1_PACKAGE2 0x2
+#define STRATOSPHERE_INI1_MAX 0x3
ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware);
+ini1_header_t *stratosphere_get_sd_files_ini1(void);
void stratosphere_free_ini1(void);
ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, unsigned int num_inis);
diff --git a/stratosphere/Makefile b/stratosphere/Makefile
index 1c1e5b1e5..559327b9e 100644
--- a/stratosphere/Makefile
+++ b/stratosphere/Makefile
@@ -1,4 +1,4 @@
-KIPS := loader pm sm boot fs_mitm creport
+KIPS := loader pm sm boot fs_mitm set_mitm creport
#TODO: boot2 ?
diff --git a/stratosphere/boot/Makefile b/stratosphere/boot/Makefile
index b4ff11df6..57b69f6e0 100644
--- a/stratosphere/boot/Makefile
+++ b/stratosphere/boot/Makefile
@@ -21,7 +21,7 @@ TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
-INCLUDES := include
+INCLUDES := include ../../common/include
EXEFS_SRC := exefs_src
DEFINES := -DDISABLE_IPC
diff --git a/stratosphere/boot/source/boot_main.cpp b/stratosphere/boot/source/boot_main.cpp
index e636ff6c5..9eb94f170 100644
--- a/stratosphere/boot/source/boot_main.cpp
+++ b/stratosphere/boot/source/boot_main.cpp
@@ -83,6 +83,8 @@ void __appInit(void) {
fatalSimple(0xCAFE << 4 | 2);
fsdevMountSdmc();
+
+ CheckAtmosphereVersion();
}
void __appExit(void) {
diff --git a/stratosphere/creport/Makefile b/stratosphere/creport/Makefile
index 67fd1e2f4..c042e968d 100644
--- a/stratosphere/creport/Makefile
+++ b/stratosphere/creport/Makefile
@@ -21,7 +21,7 @@ TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
-INCLUDES := include
+INCLUDES := include ../../common/include
EXEFS_SRC := exefs_src
DEFINES := -DDISABLE_IPC
diff --git a/stratosphere/fs_mitm/Makefile b/stratosphere/fs_mitm/Makefile
index 41a6cf79f..e8b1f047a 100644
--- a/stratosphere/fs_mitm/Makefile
+++ b/stratosphere/fs_mitm/Makefile
@@ -21,7 +21,7 @@ TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
-INCLUDES := include
+INCLUDES := include ../../common/include
EXEFS_SRC := exefs_src
DEFINES := -DDISABLE_IPC
diff --git a/stratosphere/fs_mitm/fs_mitm.json b/stratosphere/fs_mitm/fs_mitm.json
index 7733b4bbf..d90a68b96 100644
--- a/stratosphere/fs_mitm/fs_mitm.json
+++ b/stratosphere/fs_mitm/fs_mitm.json
@@ -13,63 +13,64 @@
{
"type": "syscalls",
"value": {
- "svcSetHeapSize": "0x01",
- "svcSetMemoryPermission": "0x02",
- "svcSetMemoryAttribute": "0x03",
- "svcMapMemory": "0x04",
- "svcUnmapMemory": "0x05",
- "svcQueryMemory": "0x06",
- "svcExitProcess": "0x07",
- "svcCreateThread": "0x08",
- "svcStartThread": "0x09",
- "svcExitThread": "0x0a",
- "svcSleepThread": "0x0b",
- "svcGetThreadPriority": "0x0c",
- "svcSetThreadPriority": "0x0d",
- "svcGetThreadCoreMask": "0x0e",
- "svcSetThreadCoreMask": "0x0f",
- "svcGetCurrentProcessorNumber": "0x10",
- "svcSignalEvent": "0x11",
- "svcClearEvent": "0x12",
- "svcMapSharedMemory": "0x13",
- "svcUnmapSharedMemory": "0x14",
- "svcCreateTransferMemory": "0x15",
- "svcCloseHandle": "0x16",
- "svcResetSignal": "0x17",
- "svcWaitSynchronization": "0x18",
- "svcCancelSynchronization": "0x19",
- "svcArbitrateLock": "0x1a",
- "svcArbitrateUnlock": "0x1b",
- "svcWaitProcessWideKeyAtomic": "0x1c",
- "svcSignalProcessWideKey": "0x1d",
- "svcGetSystemTick": "0x1e",
- "svcConnectToNamedPort": "0x1f",
- "svcSendSyncRequestLight": "0x20",
- "svcSendSyncRequest": "0x21",
- "svcSendSyncRequestWithUserBuffer": "0x22",
- "svcSendAsyncRequestWithUserBuffer": "0x23",
- "svcGetProcessId": "0x24",
- "svcGetThreadId": "0x25",
- "svcBreak": "0x26",
- "svcOutputDebugString": "0x27",
- "svcReturnFromException": "0x28",
- "svcGetInfo": "0x29",
- "svcWaitForAddress": "0x34",
- "svcSignalToAddress": "0x35",
- "svcCreateSession": "0x40",
- "svcAcceptSession": "0x41",
- "svcReplyAndReceiveLight": "0x42",
- "svcReplyAndReceive": "0x43",
- "svcReplyAndReceiveWithUserBuffer": "0x44",
- "svcCreateEvent": "0x45",
- "svcCreateInterruptEvent": "0x53",
- "svcQueryIoMapping": "0x55",
- "svcCreateDeviceAddressSpace": "0x56",
- "svcAttachDeviceAddressSpace": "0x57",
- "svcDetachDeviceAddressSpace": "0x58",
- "svcMapDeviceAddressSpaceAligned": "0x5a",
- "svcUnmapDeviceAddressSpace": "0x5c",
- "svcGetSystemInfo": "0x6f"
+ "svcSetHeapSize": "0x01",
+ "svcSetMemoryPermission": "0x02",
+ "svcSetMemoryAttribute": "0x03",
+ "svcMapMemory": "0x04",
+ "svcUnmapMemory": "0x05",
+ "svcQueryMemory": "0x06",
+ "svcExitProcess": "0x07",
+ "svcCreateThread": "0x08",
+ "svcStartThread": "0x09",
+ "svcExitThread": "0x0a",
+ "svcSleepThread": "0x0b",
+ "svcGetThreadPriority": "0x0c",
+ "svcSetThreadPriority": "0x0d",
+ "svcGetThreadCoreMask": "0x0e",
+ "svcSetThreadCoreMask": "0x0f",
+ "svcGetCurrentProcessorNumber": "0x10",
+ "svcSignalEvent": "0x11",
+ "svcClearEvent": "0x12",
+ "svcMapSharedMemory": "0x13",
+ "svcUnmapSharedMemory": "0x14",
+ "svcCreateTransferMemory": "0x15",
+ "svcCloseHandle": "0x16",
+ "svcResetSignal": "0x17",
+ "svcWaitSynchronization": "0x18",
+ "svcCancelSynchronization": "0x19",
+ "svcArbitrateLock": "0x1a",
+ "svcArbitrateUnlock": "0x1b",
+ "svcWaitProcessWideKeyAtomic": "0x1c",
+ "svcSignalProcessWideKey": "0x1d",
+ "svcGetSystemTick": "0x1e",
+ "svcConnectToNamedPort": "0x1f",
+ "svcSendSyncRequestLight": "0x20",
+ "svcSendSyncRequest": "0x21",
+ "svcSendSyncRequestWithUserBuffer": "0x22",
+ "svcSendAsyncRequestWithUserBuffer": "0x23",
+ "svcGetProcessId": "0x24",
+ "svcGetThreadId": "0x25",
+ "svcBreak": "0x26",
+ "svcOutputDebugString": "0x27",
+ "svcReturnFromException": "0x28",
+ "svcGetInfo": "0x29",
+ "svcWaitForAddress": "0x34",
+ "svcSignalToAddress": "0x35",
+ "svcCreateSession": "0x40",
+ "svcAcceptSession": "0x41",
+ "svcReplyAndReceiveLight": "0x42",
+ "svcReplyAndReceive": "0x43",
+ "svcReplyAndReceiveWithUserBuffer": "0x44",
+ "svcCreateEvent": "0x45",
+ "svcCreateInterruptEvent": "0x53",
+ "svcReadWriteRegister": "0x4E",
+ "svcQueryIoMapping": "0x55",
+ "svcCreateDeviceAddressSpace": "0x56",
+ "svcAttachDeviceAddressSpace": "0x57",
+ "svcDetachDeviceAddressSpace": "0x58",
+ "svcMapDeviceAddressSpaceAligned": "0x5a",
+ "svcUnmapDeviceAddressSpace": "0x5c",
+ "svcGetSystemInfo": "0x6f"
}
}
]
diff --git a/stratosphere/fs_mitm/source/debug.cpp b/stratosphere/fs_mitm/source/debug.cpp
index 35844b65e..1531eff48 100644
--- a/stratosphere/fs_mitm/source/debug.cpp
+++ b/stratosphere/fs_mitm/source/debug.cpp
@@ -24,7 +24,5 @@ void Reboot() {
}
void Log(const void *data, int size) {
- (void)(data);
- (void)(size);
/* ... */
}
\ No newline at end of file
diff --git a/stratosphere/fs_mitm/source/fs_istorage.hpp b/stratosphere/fs_mitm/source/fs_istorage.hpp
index d6827839f..2f21b6b7e 100644
--- a/stratosphere/fs_mitm/source/fs_istorage.hpp
+++ b/stratosphere/fs_mitm/source/fs_istorage.hpp
@@ -21,21 +21,19 @@
#include "debug.hpp"
-enum class FsIStorageCmd {
- Read = 0,
- Write = 1,
- Flush = 2,
- SetSize = 3,
- GetSize = 4,
- OperateRange = 5,
+enum FsIStorageCmd : u32 {
+ FsIStorageCmd_Read = 0,
+ FsIStorageCmd_Write = 1,
+ FsIStorageCmd_Flush = 2,
+ FsIStorageCmd_SetSize = 3,
+ FsIStorageCmd_GetSize = 4,
+ FsIStorageCmd_OperateRange = 5,
};
class IStorage {
public:
virtual ~IStorage();
-
- virtual IStorage *Clone() = 0;
-
+
virtual Result Read(void *buffer, size_t size, u64 offset) = 0;
virtual Result Write(void *buffer, size_t size, u64 offset) = 0;
virtual Result Flush() = 0;
@@ -51,88 +49,59 @@ class IStorageInterface : public IServiceObject {
IStorageInterface(IStorage *s) : base_storage(s) {
/* ... */
};
-
- IStorageInterface *clone() override {
- return new IStorageInterface(this->base_storage->Clone());
- }
-
+
~IStorageInterface() {
delete base_storage;
};
- Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) final {
- Result rc = 0xF601;
- switch ((FsIStorageCmd)cmd_id) {
- case FsIStorageCmd::Read:
- rc = WrapIpcCommandImpl<&IStorageInterface::read>(this, r, out_c, pointer_buffer, pointer_buffer_size);
- break;
- case FsIStorageCmd::Write:
- rc = WrapIpcCommandImpl<&IStorageInterface::write>(this, r, out_c, pointer_buffer, pointer_buffer_size);
- break;
- case FsIStorageCmd::Flush:
- rc = WrapIpcCommandImpl<&IStorageInterface::flush>(this, r, out_c, pointer_buffer, pointer_buffer_size);
- break;
- case FsIStorageCmd::SetSize:
- rc = WrapIpcCommandImpl<&IStorageInterface::set_size>(this, r, out_c, pointer_buffer, pointer_buffer_size);
- break;
- case FsIStorageCmd::GetSize:
- rc = WrapIpcCommandImpl<&IStorageInterface::get_size>(this, r, out_c, pointer_buffer, pointer_buffer_size);
- break;
- case FsIStorageCmd::OperateRange:
- if (kernelAbove400()) {
- rc = WrapIpcCommandImpl<&IStorageInterface::operate_range>(this, r, out_c, pointer_buffer, pointer_buffer_size);
- }
- break;
- default:
- break;
- }
- return rc;
- };
-
- Result handle_deferred() final {
- /* TODO: Panic, we can never defer. */
- return 0;
- };
private:
/* Actual command API. */
- virtual std::tuple read(OutBuffer buffer, u64 offset, u64 size) final {
- return {this->base_storage->Read(buffer.buffer, std::min(buffer.num_elements, size), offset)};
+ virtual Result Read(OutBuffer buffer, u64 offset, u64 size) final {
+ return this->base_storage->Read(buffer.buffer, std::min(buffer.num_elements, size), offset);
};
- virtual std::tuple write(InBuffer buffer, u64 offset, u64 size) final {
- return {this->base_storage->Write(buffer.buffer, std::min(buffer.num_elements, size), offset)};
+ virtual Result Write(InBuffer buffer, u64 offset, u64 size) final {
+ return this->base_storage->Write(buffer.buffer, std::min(buffer.num_elements, size), offset);
};
- virtual std::tuple flush() final {
- return {this->base_storage->Flush()};
+ virtual Result Flush() final {
+ return this->base_storage->Flush();
};
- virtual std::tuple set_size(u64 size) final {
- return {this->base_storage->SetSize(size)};
+ virtual Result SetSize(u64 size) final {
+ return this->base_storage->SetSize(size);
};
- virtual std::tuple get_size() final {
- u64 out_size = 0;
- Result rc = this->base_storage->GetSize(&out_size);
- return {rc, out_size};
+ virtual Result GetSize(Out size) final {
+ return this->base_storage->GetSize(size.GetPointer());
};
- virtual std::tuple operate_range(u32 operation_type, u64 offset, u64 size) final {
- FsRangeInfo out_range_info = {0};
- Result rc = this->base_storage->OperateRange(operation_type, offset, size, &out_range_info);
- return {rc, out_range_info};
+ virtual Result OperateRange(Out range_info, u32 operation_type, u64 offset, u64 size) final {
+ return this->base_storage->OperateRange(operation_type, offset, size, range_info.GetPointer());
+ };
+ public:
+ DEFINE_SERVICE_DISPATCH_TABLE {
+ /* 1.0.0- */
+ MakeServiceCommandMeta(),
+ MakeServiceCommandMeta(),
+ MakeServiceCommandMeta(),
+ MakeServiceCommandMeta(),
+ MakeServiceCommandMeta(),
+
+ /* 4.0.0- */
+ MakeServiceCommandMeta(),
};
};
class IROStorage : public IStorage {
public:
virtual Result Read(void *buffer, size_t size, u64 offset) = 0;
- Result Write(void *buffer, size_t size, u64 offset) final {
+ virtual Result Write(void *buffer, size_t size, u64 offset) final {
(void)(buffer);
(void)(offset);
(void)(size);
return 0x313802;
};
- Result Flush() final {
+ virtual Result Flush() final {
return 0x0;
};
- Result SetSize(u64 size) final {
+ virtual Result SetSize(u64 size) final {
(void)(size);
return 0x313802;
};
diff --git a/stratosphere/fs_mitm/source/fs_shim.c b/stratosphere/fs_mitm/source/fs_shim.c
index 0c510dbba..134d83498 100644
--- a/stratosphere/fs_mitm/source/fs_shim.c
+++ b/stratosphere/fs_mitm/source/fs_shim.c
@@ -17,47 +17,6 @@
#include
#include "fs_shim.h"
-/* Necessary evil. */
-Result ipcCopyFromDomain(Handle session, u32 object_id, Service *out) {
- u32* buf = (u32*)armGetTls();
-
- IpcCommand c;
- ipcInitialize(&c);
-
- struct {
- u64 magic;
- u64 cmd_id;
- u32 object_id;
- } *raw;
-
- raw = ipcPrepareHeader(&c, sizeof(*raw));
- buf[0] = IpcCommandType_Control;
- raw->magic = SFCI_MAGIC;
- raw->cmd_id = 1;
- raw->object_id = object_id;
-
- Result rc = ipcDispatch(session);
-
- if (R_SUCCEEDED(rc)) {
- IpcParsedCommand r;
- ipcParse(&r);
-
- struct ipcCopyFromDomainResponse {
- u64 magic;
- u64 result;
- } *raw = (struct ipcCopyFromDomainResponse*)r.Raw;
-
- rc = raw->result;
-
- if (R_SUCCEEDED(rc)) {
- serviceCreate(out, r.Handles[0]);
- }
- }
-
- return rc;
-}
-
-
/* Missing fsp-srv commands. */
Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out) {
IpcCommand c;
@@ -68,7 +27,7 @@ Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out) {
u64 cmd_id;
} *raw;
- raw = ipcPrepareHeader(&c, sizeof(*raw));
+ raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 200;
@@ -77,60 +36,25 @@ Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out) {
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
- ipcParse(&r);
-
struct {
u64 magic;
u64 result;
- } *resp = r.Raw;
+ } *resp;
+
+ serviceIpcParse(s, &r, sizeof(*resp));
+ resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
- serviceCreate(&out->s, r.Handles[0]);
+ serviceCreateSubservice(&out->s, s, &r, 0);
}
}
return rc;
}
-Result fsOpenDataStorageByCurrentProcessFromDomainFwd(Service* s, u32 *out_object_id) {
- IpcCommand c;
- ipcInitialize(&c);
-
- struct {
- u64 magic;
- u64 cmd_id;
- } *raw;
-
- raw = ipcPrepareHeaderForDomain(&c, sizeof(*raw), s->object_id);
-
- raw->magic = SFCI_MAGIC;
- raw->cmd_id = 200;
-
- Result rc = serviceIpcDispatch(s);
-
- if (R_SUCCEEDED(rc)) {
- IpcParsedCommand r;
- ipcParseForDomain(&r);
-
- struct {
- u64 magic;
- u64 result;
- u32 object_id;
- } *resp = r.Raw;
-
- rc = resp->result;
-
- if (R_SUCCEEDED(rc)) {
- *out_object_id = resp->object_id;
- }
- }
-
- return rc;
-}
-
-Result fsOpenDataStorageByDataId(Service* s, FsStorageId storage_id, u64 data_id, FsStorage* out) {
+Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorageId storage_id, u64 data_id, FsStorage* out) {
IpcCommand c;
ipcInitialize(&c);
@@ -141,7 +65,7 @@ Result fsOpenDataStorageByDataId(Service* s, FsStorageId storage_id, u64 data_id
u64 data_id;
} *raw;
- raw = ipcPrepareHeader(&c, sizeof(*raw));
+ raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 202;
@@ -152,58 +76,18 @@ Result fsOpenDataStorageByDataId(Service* s, FsStorageId storage_id, u64 data_id
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
- ipcParse(&r);
-
struct {
u64 magic;
u64 result;
- } *resp = r.Raw;
+ } *resp;
+
+ serviceIpcParse(s, &r, sizeof(*resp));
+ resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
- serviceCreate(&out->s, r.Handles[0]);
- }
- }
-
- return rc;
-}
-
-
-Result fsOpenDataStorageByDataIdFromDomain(Service* s, FsStorageId storage_id, u64 data_id, u32 *out_object_id) {
- IpcCommand c;
- ipcInitialize(&c);
-
- struct {
- u64 magic;
- u64 cmd_id;
- FsStorageId storage_id;
- u64 data_id;
- } *raw;
-
- raw = ipcPrepareHeaderForDomain(&c, sizeof(*raw), s->object_id);
-
- raw->magic = SFCI_MAGIC;
- raw->cmd_id = 202;
- raw->storage_id = storage_id;
- raw->data_id = data_id;
-
- Result rc = serviceIpcDispatch(s);
-
- if (R_SUCCEEDED(rc)) {
- IpcParsedCommand r;
- ipcParseForDomain(&r);
-
- struct {
- u64 magic;
- u64 result;
- u32 object_id;
- } *resp = r.Raw;
-
- rc = resp->result;
-
- if (R_SUCCEEDED(rc)) {
- *out_object_id = resp->object_id;
+ serviceCreateSubservice(&out->s, s, &r, 0);
}
}
@@ -223,7 +107,7 @@ Result fsFileOperateRange(FsFile* f, u32 op_id, u64 off, u64 len, FsRangeInfo *o
u64 len;
} *raw;
- raw = ipcPrepareHeader(&c, sizeof(*raw));
+ raw = serviceIpcPrepareHeader(&f->s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 5;
@@ -235,13 +119,14 @@ Result fsFileOperateRange(FsFile* f, u32 op_id, u64 off, u64 len, FsRangeInfo *o
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
- ipcParse(&r);
-
struct {
u64 magic;
u64 result;
FsRangeInfo range_info;
- } *resp = r.Raw;
+ } *resp;
+
+ serviceIpcParse(&f->s, &r, sizeof(*resp));
+ resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc) && out) *out = resp->range_info;
@@ -263,7 +148,7 @@ Result fsStorageOperateRange(FsStorage* s, u32 op_id, u64 off, u64 len, FsRangeI
u64 len;
} *raw;
- raw = ipcPrepareHeader(&c, sizeof(*raw));
+ raw = serviceIpcPrepareHeader(&s->s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 5;
@@ -275,13 +160,14 @@ Result fsStorageOperateRange(FsStorage* s, u32 op_id, u64 off, u64 len, FsRangeI
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
- ipcParse(&r);
-
struct {
u64 magic;
u64 result;
FsRangeInfo range_info;
- } *resp = r.Raw;
+ } *resp;
+
+ serviceIpcParse(&s->s, &r, sizeof(*resp));
+ resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc) && out) *out = resp->range_info;
diff --git a/stratosphere/fs_mitm/source/fs_shim.h b/stratosphere/fs_mitm/source/fs_shim.h
index 9030078e7..d73c2595b 100644
--- a/stratosphere/fs_mitm/source/fs_shim.h
+++ b/stratosphere/fs_mitm/source/fs_shim.h
@@ -16,14 +16,9 @@ typedef struct {
u32 flags[0x40/sizeof(u32)];
} FsRangeInfo;
-/* Necessary evils. */
-Result ipcCopyFromDomain(Handle session, u32 object_id, Service *out);
-
/* Missing fsp-srv commands. */
Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out);
-Result fsOpenDataStorageByCurrentProcessFromDomainFwd(Service* s, u32 *out_object_id);
-Result fsOpenDataStorageByDataId(Service* s, FsStorageId storage_id, u64 data_id, FsStorage* out);
-Result fsOpenDataStorageByDataIdFromDomain(Service* s, FsStorageId storage_id, u64 data_id, u32 *out_object_id);
+Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorageId storage_id, u64 data_id, FsStorage* out);
/* Missing FS File commands. */
Result fsFileOperateRange(FsFile* f, u32 op_id, u64 off, u64 len, FsRangeInfo *out);
diff --git a/stratosphere/fs_mitm/source/fsmitm_layeredrom.cpp b/stratosphere/fs_mitm/source/fsmitm_layeredrom.cpp
index 4a11580fa..ed6ad63a1 100644
--- a/stratosphere/fs_mitm/source/fsmitm_layeredrom.cpp
+++ b/stratosphere/fs_mitm/source/fsmitm_layeredrom.cpp
@@ -124,12 +124,14 @@ Result LayeredRomFS::Read(void *buffer, size_t size, u64 offset) {
fatalSimple(0xF601);
}
read_so_far += cur_read_size;
+ offset += cur_read_size;
} else {
/* Handle padding explicitly. */
cur_source_ind++;
/* Zero out the padding we skip, here. */
- memset((void *)((uintptr_t)buffer + read_so_far), 0, ((*this->p_source_infos)[cur_source_ind]).virtual_offset - (cur_source->virtual_offset + cur_source->size));
- read_so_far += ((*this->p_source_infos)[cur_source_ind]).virtual_offset - (cur_source->virtual_offset + cur_source->size);
+ memset((void *)((uintptr_t)buffer + read_so_far), 0, ((*this->p_source_infos)[cur_source_ind]).virtual_offset - offset);
+ read_so_far += ((*this->p_source_infos)[cur_source_ind]).virtual_offset - offset;
+ offset = ((*this->p_source_infos)[cur_source_ind]).virtual_offset;
}
}
diff --git a/stratosphere/fs_mitm/source/fsmitm_layeredrom.hpp b/stratosphere/fs_mitm/source/fsmitm_layeredrom.hpp
index 3e6061651..01be3af01 100644
--- a/stratosphere/fs_mitm/source/fsmitm_layeredrom.hpp
+++ b/stratosphere/fs_mitm/source/fsmitm_layeredrom.hpp
@@ -32,16 +32,12 @@ class LayeredRomFS : public IROStorage {
/* Information about the merged RomFS. */
u64 title_id;
std::shared_ptr> p_source_infos;
-
- LayeredRomFS *Clone() override {
- return new LayeredRomFS(*this);
- };
public:
LayeredRomFS(std::shared_ptr s_r, std::shared_ptr f_r, u64 tid);
virtual ~LayeredRomFS() = default;
- Result Read(void *buffer, size_t size, u64 offset) override;
- Result GetSize(u64 *out_size) override;
- Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override;
+ virtual Result Read(void *buffer, size_t size, u64 offset) override;
+ virtual Result GetSize(u64 *out_size) override;
+ virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override;
};
diff --git a/stratosphere/fs_mitm/source/fsmitm_main.cpp b/stratosphere/fs_mitm/source/fsmitm_main.cpp
index e6271b10d..1c12d9db1 100644
--- a/stratosphere/fs_mitm/source/fsmitm_main.cpp
+++ b/stratosphere/fs_mitm/source/fsmitm_main.cpp
@@ -22,13 +22,7 @@
#include
#include
-#include "sm_mitm.h"
-
-#include "mitm_server.hpp"
#include "fsmitm_service.hpp"
-#include "fsmitm_worker.hpp"
-
-#include "mitm_query_service.hpp"
#include "fsmitm_utils.hpp"
@@ -67,58 +61,32 @@ void __appInit(void) {
fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_SM));
}
- rc = smMitMInitialize();
- if (R_FAILED(rc)) {
- fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_SM));
- }
-
rc = fsInitialize();
if (R_FAILED(rc)) {
fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_FS));
}
-
- rc = splInitialize();
- if (R_FAILED(rc)) {
- fatalSimple(0xCAFE << 4 | 3);
- }
-
- /* Check for exosphere API compatibility. */
- u64 exosphere_cfg;
- if (R_SUCCEEDED(splGetConfig((SplConfigItem)65000, &exosphere_cfg))) {
- /* MitM requires Atmosphere API 0.1. */
- u16 api_version = (exosphere_cfg >> 16) & 0xFFFF;
- if (api_version < 0x0001) {
- fatalSimple(0xCAFE << 4 | 0xFE);
- }
- } else {
- fatalSimple(0xCAFE << 4 | 0xFF);
- }
-
- //splExit();
+ CheckAtmosphereVersion();
}
void __appExit(void) {
/* Cleanup services. */
fsExit();
- smMitMExit();
smExit();
}
+struct FsMitmManagerOptions {
+ static const size_t PointerBufferSize = 0x800;
+ static const size_t MaxDomains = 0x10;
+ static const size_t MaxDomainObjects = 0x4000;
+};
+using FsMitmManager = WaitableManager;
+
int main(int argc, char **argv)
{
- Thread worker_thread = {0};
Thread sd_initializer_thread = {0};
+ Thread hid_initializer_thread = {0};
consoleDebugInit(debugDevice_SVC);
-
- consoleDebugInit(debugDevice_SVC);
-
- if (R_FAILED(threadCreate(&worker_thread, &FsMitMWorker::Main, NULL, 0x20000, 45, 0))) {
- /* TODO: Panic. */
- }
- if (R_FAILED(threadStart(&worker_thread))) {
- /* TODO: Panic. */
- }
-
+
if (R_FAILED(threadCreate(&sd_initializer_thread, &Utils::InitializeSdThreadFunc, NULL, 0x4000, 0x15, 0))) {
/* TODO: Panic. */
}
@@ -126,18 +94,23 @@ int main(int argc, char **argv)
/* TODO: Panic. */
}
+ if (R_FAILED(threadCreate(&hid_initializer_thread, &Utils::InitializeHidThreadFunc, NULL, 0x4000, 0x15, 0))) {
+ /* TODO: Panic. */
+ }
+ if (R_FAILED(threadStart(&hid_initializer_thread))) {
+ /* TODO: Panic. */
+ }
+
/* TODO: What's a good timeout value to use here? */
- auto server_manager = std::make_unique(1, U64_MAX, 0x20000);
- //auto server_manager = std::make_unique(U64_MAX);
+ auto server_manager = new FsMitmManager(5);
/* Create fsp-srv mitm. */
- ISession> *fs_query_srv = NULL;
- MitMServer *fs_srv = new MitMServer(&fs_query_srv, "fsp-srv", 61);
- server_manager->add_waitable(fs_srv);
- server_manager->add_waitable(fs_query_srv);
-
+ AddMitmServerToManager(server_manager, "fsp-srv", 61);
+
/* Loop forever, servicing our services. */
- server_manager->process();
+ server_manager->Process();
+
+ delete server_manager;
return 0;
}
diff --git a/stratosphere/fs_mitm/source/fsmitm_romfsbuild.cpp b/stratosphere/fs_mitm/source/fsmitm_romfsbuild.cpp
index 3a1afb780..4a01bff77 100644
--- a/stratosphere/fs_mitm/source/fsmitm_romfsbuild.cpp
+++ b/stratosphere/fs_mitm/source/fsmitm_romfsbuild.cpp
@@ -400,7 +400,7 @@ void RomFSBuildContext::Build(std::vector *out_infos) {
header->file_table_ofs = header->file_hash_table_ofs + header->file_hash_table_size;
/* For debugging, uncomment this to get a log of the generated metadata tables. */
- /*
+
{
FsFileSystem sd_fs;
if (R_SUCCEEDED(fsMountSdcard(&sd_fs))) {
@@ -415,7 +415,7 @@ void RomFSBuildContext::Build(std::vector *out_infos) {
fsFsClose(&sd_fs);
}
}
- */
+
out_infos->emplace_back(header->dir_hash_table_ofs, this->dir_hash_table_size + this->dir_table_size + this->file_hash_table_size + this->file_table_size, metadata, RomFSDataSource::Memory);
}
diff --git a/stratosphere/fs_mitm/source/fsmitm_romstorage.hpp b/stratosphere/fs_mitm/source/fsmitm_romstorage.hpp
index cc378cd1f..c421a2147 100644
--- a/stratosphere/fs_mitm/source/fsmitm_romstorage.hpp
+++ b/stratosphere/fs_mitm/source/fsmitm_romstorage.hpp
@@ -35,10 +35,6 @@ class RomFileStorage : public IROStorage {
fsFileClose(base_file);
delete base_file;
};
-
- RomFileStorage *Clone() override {
- return new RomFileStorage(this->base_file);
- };
public:
Result Read(void *buffer, size_t size, u64 offset) override {
size_t out_sz = 0;
@@ -72,10 +68,6 @@ class RomInterfaceStorage : public IROStorage {
fsStorageClose(base_storage);
delete base_storage;
};
-
- RomInterfaceStorage *Clone() override {
- return new RomInterfaceStorage(this->base_storage);
- };
public:
Result Read(void *buffer, size_t size, u64 offset) override {
return fsStorageRead(this->base_storage, offset, buffer, size);
diff --git a/stratosphere/fs_mitm/source/fsmitm_service.cpp b/stratosphere/fs_mitm/source/fsmitm_service.cpp
index 07f5c3156..a9f1e30a5 100644
--- a/stratosphere/fs_mitm/source/fsmitm_service.cpp
+++ b/stratosphere/fs_mitm/source/fsmitm_service.cpp
@@ -18,149 +18,115 @@
#include "fsmitm_service.hpp"
#include "fs_shim.h"
-#include "fsmitm_worker.hpp"
#include "fsmitm_utils.hpp"
#include "fsmitm_romstorage.hpp"
#include "fsmitm_layeredrom.hpp"
-#include "mitm_query_service.hpp"
#include "debug.hpp"
-Result FsMitMService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) {
- Result rc = 0xF601;
- if (this->has_initialized) {
- switch (static_cast(cmd_id)) {
- case FspSrvCmd::OpenDataStorageByCurrentProcess:
- rc = WrapIpcCommandImpl<&FsMitMService::open_data_storage_by_current_process>(this, r, out_c, pointer_buffer, pointer_buffer_size);
+void FsMitmService::PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx) {
+ auto this_ptr = static_cast(obj);
+ switch ((FspSrvCmd)ctx->cmd_id) {
+ case FspSrvCmd_SetCurrentProcess:
+ if (R_SUCCEEDED(ctx->rc)) {
+ this_ptr->has_initialized = true;
+ this_ptr->process_id = ctx->request.Pid;
+ this_ptr->title_id = this_ptr->process_id;
+ if (R_FAILED(MitmQueryUtils::GetAssociatedTidForPid(this_ptr->process_id, &this_ptr->title_id))) {
+ /* Log here, if desired. */
+ }
break;
- case FspSrvCmd::OpenDataStorageByDataId:
- rc = WrapIpcCommandImpl<&FsMitMService::open_data_storage_by_data_id>(this, r, out_c, pointer_buffer, pointer_buffer_size);
- break;
- default:
- break;
- }
- } else {
- if (static_cast(cmd_id) == FspSrvCmd::SetCurrentProcess) {
- if (r.HasPid) {
- this->init_pid = r.Pid;
}
- }
- }
- return rc;
-}
-
-void FsMitMService::postprocess(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) {
- struct {
- u64 magic;
- u64 result;
- } *resp = (decltype(resp))r.Raw;
-
- u64 *tls = (u64 *)armGetTls();
- std::array backup_tls;
- std::copy(tls, tls + backup_tls.size(), backup_tls.begin());
-
- Result rc = (Result)resp->result;
- switch (static_cast(cmd_id)) {
- case FspSrvCmd::SetCurrentProcess:
- if (R_SUCCEEDED(rc)) {
- this->has_initialized = true;
- }
- this->process_id = this->init_pid;
- this->title_id = this->process_id;
- if (R_FAILED(MitMQueryUtils::get_associated_tid_for_pid(this->process_id, &this->title_id))) {
- /* Log here, if desired. */
- }
- std::copy(backup_tls.begin(), backup_tls.end(), tls);
break;
default:
break;
}
- resp->result = rc;
-}
-
-Result FsMitMService::handle_deferred() {
- /* This service is never deferrable. */
- return 0;
}
/* Add redirection for RomFS to the SD card. */
-std::tuple> FsMitMService::open_data_storage_by_current_process() {
- IPCSession *out_session = NULL;
- std::shared_ptr out_storage = nullptr;
+Result FsMitmService::OpenDataStorageByCurrentProcess(Out> out_storage) {
+ std::shared_ptr storage = nullptr;
u32 out_domain_id = 0;
- Result rc;
+ Result rc = 0;
+
+ ON_SCOPE_EXIT {
+ if (R_SUCCEEDED(rc)) {
+ out_storage.SetValue(std::move(storage));
+ if (out_storage.IsDomain()) {
+ out_storage.ChangeObjectId(out_domain_id);
+ }
+ }
+ };
+
if (this->romfs_storage != nullptr) {
- if (this->get_owner() != NULL) {
- rc = fsOpenDataStorageByCurrentProcessFromDomainFwd(this->forward_service, &out_domain_id);
+ if (out_storage.IsDomain()) {
+ FsStorage s = {0};
+ rc = fsOpenDataStorageByCurrentProcessFwd(this->forward_service.get(), &s);
+ if (R_SUCCEEDED(rc)) {
+ out_domain_id = s.s.object_id;
+ }
} else {
rc = 0;
}
if (R_SUCCEEDED(rc)) {
- out_storage = this->romfs_storage;
- out_session = new IPCSession(out_storage);
+ storage = this->romfs_storage;
}
} else {
FsStorage data_storage;
FsFile data_file;
+
+ rc = fsOpenDataStorageByCurrentProcessFwd(this->forward_service.get(), &data_storage);
- if (this->get_owner() == NULL) {
- rc = fsOpenDataStorageByCurrentProcessFwd(this->forward_service, &data_storage);
- } else {
- rc = fsOpenDataStorageByCurrentProcessFromDomainFwd(this->forward_service, &out_domain_id);
- if (R_SUCCEEDED(rc)) {
- rc = ipcCopyFromDomain(this->forward_service->handle, out_domain_id, &data_storage.s);
- }
- }
Log(armGetTls(), 0x100);
if (R_SUCCEEDED(rc)) {
/* TODO: Is there a sensible path that ends in ".romfs" we can use?" */
if (R_SUCCEEDED(Utils::OpenSdFileForAtmosphere(this->title_id, "romfs.bin", FS_OPEN_READ, &data_file))) {
- out_storage = std::make_shared(new LayeredRomFS(std::make_shared(data_storage), std::make_shared(data_file), this->title_id));
+ storage = std::make_shared(new LayeredRomFS(std::make_shared(data_storage), std::make_shared(data_file), this->title_id));
} else {
- out_storage = std::make_shared(new LayeredRomFS(std::make_shared(data_storage), nullptr, this->title_id));
+ storage = std::make_shared(new LayeredRomFS(std::make_shared(data_storage), nullptr, this->title_id));
}
- this->romfs_storage = out_storage;
- out_session = new IPCSession(out_storage);
- if (this->get_owner() == NULL) {
- FsMitMWorker::AddWaitable(out_session);
+ this->romfs_storage = storage;
+ if (out_storage.IsDomain()) {
+ out_domain_id = data_storage.s.object_id;
}
}
}
- OutSession out_s = OutSession(out_session);
- out_s.domain_id = out_domain_id;
- return {rc, out_s};
+ return rc;
}
/* Add redirection for System Data Archives to the SD card. */
-std::tuple> FsMitMService::open_data_storage_by_data_id(u64 sid, u64 data_id) {
+Result FsMitmService::OpenDataStorageByDataId(Out> out_storage, u64 data_id, u8 sid) {
FsStorageId storage_id = (FsStorageId)sid;
- IPCSession *out_session = NULL;
FsStorage data_storage;
FsFile data_file;
+
+ std::shared_ptr storage = nullptr;
u32 out_domain_id = 0;
- Result rc;
- if (this->get_owner() == NULL) {
- rc = fsOpenDataStorageByDataId(this->forward_service, storage_id, data_id, &data_storage);
- } else {
- rc = fsOpenDataStorageByDataIdFromDomain(this->forward_service, storage_id, data_id, &out_domain_id);
+ Result rc = 0;
+
+ ON_SCOPE_EXIT {
if (R_SUCCEEDED(rc)) {
- rc = ipcCopyFromDomain(this->forward_service->handle, out_domain_id, &data_storage.s);
+ out_storage.SetValue(std::move(storage));
+ if (out_storage.IsDomain()) {
+ out_storage.ChangeObjectId(out_domain_id);
+ }
}
- }
+ };
+
+ rc = fsOpenDataStorageByDataIdFwd(this->forward_service.get(), storage_id, data_id, &data_storage);
+
if (R_SUCCEEDED(rc)) {
/* TODO: Is there a sensible path that ends in ".romfs" we can use?" */
if (R_SUCCEEDED(Utils::OpenSdFileForAtmosphere(data_id, "romfs.bin", FS_OPEN_READ, &data_file))) {
- out_session = new IPCSession(std::make_shared(new LayeredRomFS(std::make_shared(data_storage), std::make_shared(data_file), data_id)));
+ storage = std::make_shared(new LayeredRomFS(std::make_shared(data_storage), std::make_shared(data_file), data_id));
} else {
- out_session = new IPCSession(std::make_shared(new LayeredRomFS(std::make_shared(data_storage), nullptr, data_id)));
+ storage = std::make_shared(new LayeredRomFS(std::make_shared(data_storage), nullptr, data_id));
}
- if (this->get_owner() == NULL) {
- FsMitMWorker::AddWaitable(out_session);
+ if (out_storage.IsDomain()) {
+ out_domain_id = data_storage.s.object_id;
}
}
- OutSession out_s = OutSession(out_session);
- out_s.domain_id = out_domain_id;
- return {rc, out_s};
+ return rc;
}
\ No newline at end of file
diff --git a/stratosphere/fs_mitm/source/fsmitm_service.hpp b/stratosphere/fs_mitm/source/fsmitm_service.hpp
index b1bd62d9c..75dc7b426 100644
--- a/stratosphere/fs_mitm/source/fsmitm_service.hpp
+++ b/stratosphere/fs_mitm/source/fsmitm_service.hpp
@@ -16,49 +16,41 @@
#pragma once
#include
-#include
-#include "imitmserviceobject.hpp"
+#include
#include "fs_istorage.hpp"
#include "fsmitm_utils.hpp"
-enum class FspSrvCmd {
- SetCurrentProcess = 1,
- OpenDataStorageByCurrentProcess = 200,
- OpenDataStorageByDataId = 202,
+enum FspSrvCmd : u32 {
+ FspSrvCmd_SetCurrentProcess = 1,
+ FspSrvCmd_OpenDataStorageByCurrentProcess = 200,
+ FspSrvCmd_OpenDataStorageByDataId = 202,
};
-class FsMitMService : public IMitMServiceObject {
+class FsMitmService : public IMitmServiceObject {
private:
bool has_initialized = false;
- u64 init_pid = 0;
std::shared_ptr romfs_storage;
public:
- FsMitMService(Service *s) : IMitMServiceObject(s) {
+ FsMitmService(std::shared_ptr s) : IMitmServiceObject(s) {
/* ... */
}
- static bool should_mitm(u64 pid, u64 tid) {
- return tid >= 0x0100000000010000ULL || Utils::HasSdMitMFlag(tid);
+ static bool ShouldMitm(u64 pid, u64 tid) {
+ if (Utils::HasSdDisableMitMFlag(tid)) {
+ return false;
+ }
+ return (tid >= 0x0100000000010000ULL || Utils::HasSdMitMFlag(tid)) && Utils::HasOverrideButton(tid);
}
- FsMitMService *clone() override {
- auto new_srv = new FsMitMService((Service *)&this->forward_service);
- this->clone_to(new_srv);
- return new_srv;
- }
-
- void clone_to(void *o) override {
- FsMitMService *other = (FsMitMService *)o;
- other->has_initialized = has_initialized;
- other->init_pid = init_pid;
- }
-
- virtual Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size);
- virtual void postprocess(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size);
- virtual Result handle_deferred();
-
+ static void PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx);
+
protected:
/* Overridden commands. */
- std::tuple> open_data_storage_by_current_process();
- std::tuple> open_data_storage_by_data_id(u64 storage_id, u64 data_id);
+ Result OpenDataStorageByCurrentProcess(Out> out);
+ Result OpenDataStorageByDataId(Out> out, u64 data_id, u8 storage_id);
+ public:
+ DEFINE_SERVICE_DISPATCH_TABLE {
+ MakeServiceCommandMeta(),
+ MakeServiceCommandMeta(),
+ };
};
diff --git a/stratosphere/fs_mitm/source/fsmitm_utils.cpp b/stratosphere/fs_mitm/source/fsmitm_utils.cpp
index a98a1b739..aa22bb5c3 100644
--- a/stratosphere/fs_mitm/source/fsmitm_utils.cpp
+++ b/stratosphere/fs_mitm/source/fsmitm_utils.cpp
@@ -18,14 +18,23 @@
#include
#include
#include
+#include
-#include "sm_mitm.h"
#include "debug.hpp"
#include "fsmitm_utils.hpp"
+#include "ini.h"
static FsFileSystem g_sd_filesystem = {0};
static std::vector g_mitm_flagged_tids;
+static std::vector g_disable_mitm_flagged_tids;
static std::atomic_bool g_has_initialized = false;
+static std::atomic_bool g_has_hid_session = false;
+
+static u64 g_override_key_combination = KEY_R;
+static bool g_override_by_default = true;
+
+/* Static buffer for loader.ini contents at runtime. */
+static char g_config_ini_data[0x800];
static bool IsHexadecimal(const char *str) {
while (*str) {
@@ -72,20 +81,45 @@ void Utils::InitializeSdThreadFunc(void *args) {
g_mitm_flagged_tids.push_back(title_id);
fsFileClose(&f);
}
+
+ memset(title_path, 0, sizeof(title_path));
+ strcpy(title_path, "/atmosphere/titles/");
+ strcat(title_path, dir_entry.name);
+ strcat(title_path, "/fsmitm_disable.flag");
+ if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, title_path, FS_OPEN_READ, &f))) {
+ g_disable_mitm_flagged_tids.push_back(title_id);
+ fsFileClose(&f);
+ }
}
}
fsDirClose(&titles_dir);
}
+ Utils::RefreshConfiguration();
+
g_has_initialized = true;
svcExitThread();
}
+void Utils::InitializeHidThreadFunc(void *args) {
+ while (R_FAILED(hidInitialize())) {
+ svcSleepThread(1000ULL);
+ }
+
+ g_has_hid_session = true;
+
+ svcExitThread();
+}
+
bool Utils::IsSdInitialized() {
return g_has_initialized;
}
+bool Utils::IsHidInitialized() {
+ return g_has_hid_session;
+}
+
Result Utils::OpenSdFile(const char *fn, int flags, FsFile *out) {
if (!IsSdInitialized()) {
return 0xFA202;
@@ -189,3 +223,115 @@ bool Utils::HasSdMitMFlag(u64 tid) {
}
return false;
}
+
+bool Utils::HasSdDisableMitMFlag(u64 tid) {
+ if (IsSdInitialized()) {
+ return std::find(g_disable_mitm_flagged_tids.begin(), g_disable_mitm_flagged_tids.end(), tid) != g_disable_mitm_flagged_tids.end();
+ }
+ return false;
+}
+
+Result Utils::GetKeysDown(u64 *keys) {
+ if (!Utils::IsHidInitialized()) {
+ return MAKERESULT(Module_Libnx, LibnxError_InitFail_HID);
+ }
+
+ hidScanInput();
+ *keys = hidKeysDown(CONTROLLER_P1_AUTO);
+
+ return 0x0;
+}
+
+bool Utils::HasOverrideButton(u64 tid) {
+ if (tid < 0x0100000000010000ULL || !IsSdInitialized()) {
+ /* Disable button override disable for non-applications. */
+ return true;
+ }
+
+ /* Unconditionally refresh loader.ini contents. */
+ RefreshConfiguration();
+
+ u64 kDown = 0;
+ bool keys_triggered = (R_SUCCEEDED(GetKeysDown(&kDown)) && ((kDown & g_override_key_combination) != 0));
+ return IsSdInitialized() && (g_override_by_default ^ keys_triggered);
+}
+
+static int FsMitMIniHandler(void *user, const char *section, const char *name, const char *value) {
+ /* Taken and modified, with love, from Rajkosto's implementation. */
+ if (strcasecmp(section, "config") == 0) {
+ if (strcasecmp(name, "override_key") == 0) {
+ if (value[0] == '!') {
+ g_override_by_default = true;
+ value++;
+ } else {
+ g_override_by_default = false;
+ }
+
+ if (strcasecmp(value, "A") == 0) {
+ g_override_key_combination = KEY_A;
+ } else if (strcasecmp(value, "B") == 0) {
+ g_override_key_combination = KEY_B;
+ } else if (strcasecmp(value, "X") == 0) {
+ g_override_key_combination = KEY_X;
+ } else if (strcasecmp(value, "Y") == 0) {
+ g_override_key_combination = KEY_Y;
+ } else if (strcasecmp(value, "LS") == 0) {
+ g_override_key_combination = KEY_LSTICK;
+ } else if (strcasecmp(value, "RS") == 0) {
+ g_override_key_combination = KEY_RSTICK;
+ } else if (strcasecmp(value, "L") == 0) {
+ g_override_key_combination = KEY_L;
+ } else if (strcasecmp(value, "R") == 0) {
+ g_override_key_combination = KEY_R;
+ } else if (strcasecmp(value, "ZL") == 0) {
+ g_override_key_combination = KEY_ZL;
+ } else if (strcasecmp(value, "ZR") == 0) {
+ g_override_key_combination = KEY_ZR;
+ } else if (strcasecmp(value, "PLUS") == 0) {
+ g_override_key_combination = KEY_PLUS;
+ } else if (strcasecmp(value, "MINUS") == 0) {
+ g_override_key_combination = KEY_MINUS;
+ } else if (strcasecmp(value, "DLEFT") == 0) {
+ g_override_key_combination = KEY_DLEFT;
+ } else if (strcasecmp(value, "DUP") == 0) {
+ g_override_key_combination = KEY_DUP;
+ } else if (strcasecmp(value, "DRIGHT") == 0) {
+ g_override_key_combination = KEY_DRIGHT;
+ } else if (strcasecmp(value, "DDOWN") == 0) {
+ g_override_key_combination = KEY_DDOWN;
+ } else if (strcasecmp(value, "SL") == 0) {
+ g_override_key_combination = KEY_SL;
+ } else if (strcasecmp(value, "SR") == 0) {
+ g_override_key_combination = KEY_SR;
+ } else {
+ g_override_key_combination = 0;
+ }
+ }
+ } else {
+ return 0;
+ }
+ return 1;
+}
+
+
+void Utils::RefreshConfiguration() {
+ FsFile config_file;
+ if (R_FAILED(fsFsOpenFile(&g_sd_filesystem, "/atmosphere/loader.ini", FS_OPEN_READ, &config_file))) {
+ return;
+ }
+
+ u64 size;
+ if (R_FAILED(fsFileGetSize(&config_file, &size))) {
+ return;
+ }
+
+ size = std::min(size, (decltype(size))0x7FF);
+
+ /* Read in string. */
+ std::fill(g_config_ini_data, g_config_ini_data + 0x800, 0);
+ size_t r_s;
+ fsFileRead(&config_file, 0, g_config_ini_data, size, &r_s);
+ fsFileClose(&config_file);
+
+ ini_parse_string(g_config_ini_data, FsMitMIniHandler, NULL);
+}
\ No newline at end of file
diff --git a/stratosphere/fs_mitm/source/fsmitm_utils.hpp b/stratosphere/fs_mitm/source/fsmitm_utils.hpp
index e1480d0d1..42d547481 100644
--- a/stratosphere/fs_mitm/source/fsmitm_utils.hpp
+++ b/stratosphere/fs_mitm/source/fsmitm_utils.hpp
@@ -37,4 +37,13 @@ class Utils {
/* SD card Initialization + MitM detection. */
static void InitializeSdThreadFunc(void *args);
static bool HasSdMitMFlag(u64 tid);
+ static bool HasSdDisableMitMFlag(u64 tid);
+
+
+ static bool IsHidInitialized();
+ static void InitializeHidThreadFunc(void *args);
+ static Result GetKeysDown(u64 *keys);
+ static bool HasOverrideButton(u64 tid);
+ private:
+ static void RefreshConfiguration();
};
\ No newline at end of file
diff --git a/stratosphere/fs_mitm/source/fsmitm_worker.cpp b/stratosphere/fs_mitm/source/fsmitm_worker.cpp
deleted file mode 100644
index 24cba1168..000000000
--- a/stratosphere/fs_mitm/source/fsmitm_worker.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (c) 2018 Atmosphère-NX
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#include
-#include
-#include
-#include "fsmitm_worker.hpp"
-
-static SystemEvent *g_new_waitable_event = NULL;
-
-static HosMutex g_new_waitable_mutex;
-static HosSemaphore g_sema_new_waitable_finish;
-
-static std::unique_ptr g_worker_waiter;
-
-Result FsMitMWorker::AddWaitableCallback(void *arg, Handle *handles, size_t num_handles, u64 timeout) {
- (void)arg;
- svcClearEvent(handles[0]);
- g_sema_new_waitable_finish.Signal();
- return 0;
-}
-
-void FsMitMWorker::AddWaitable(IWaitable *waitable) {
- g_worker_waiter->add_waitable(waitable);
- std::scoped_lock lk{g_new_waitable_mutex};
- g_new_waitable_event->signal_event();
- g_sema_new_waitable_finish.Wait();
-}
-
-void FsMitMWorker::Main(void *arg) {
- /* Initialize waitable event. */
- g_new_waitable_event = new SystemEvent(NULL, &FsMitMWorker::AddWaitableCallback);
-
- /* Make a new waitable manager. */
- g_worker_waiter = std::make_unique(U64_MAX);
- g_worker_waiter->add_waitable(g_new_waitable_event);
-
- /* Service processes. */
- g_worker_waiter->process();
-}
diff --git a/stratosphere/fs_mitm/source/ini.c b/stratosphere/fs_mitm/source/ini.c
new file mode 100644
index 000000000..63626c72d
--- /dev/null
+++ b/stratosphere/fs_mitm/source/ini.c
@@ -0,0 +1,269 @@
+/* inih -- simple .INI file parser
+
+inih is released under the New BSD license (see LICENSE.txt). Go to the project
+home page for more info:
+
+https://github.com/benhoyt/inih
+
+*/
+
+#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include
+#include
+#include
+
+#include "ini.h"
+
+#if !INI_USE_STACK
+#include
+#endif
+
+#define MAX_SECTION 50
+#define MAX_NAME 50
+
+/* Used by ini_parse_string() to keep track of string parsing state. */
+typedef struct {
+ const char* ptr;
+ size_t num_left;
+} ini_parse_string_ctx;
+
+/* Strip whitespace chars off end of given string, in place. Return s. */
+static char* rstrip(char* s)
+{
+ char* p = s + strlen(s);
+ while (p > s && isspace((unsigned char)(*--p)))
+ *p = '\0';
+ return s;
+}
+
+/* Return pointer to first non-whitespace char in given string. */
+static char* lskip(const char* s)
+{
+ while (*s && isspace((unsigned char)(*s)))
+ s++;
+ return (char*)s;
+}
+
+/* Return pointer to first char (of chars) or inline comment in given string,
+ or pointer to null at end of string if neither found. Inline comment must
+ be prefixed by a whitespace character to register as a comment. */
+static char* find_chars_or_comment(const char* s, const char* chars)
+{
+#if INI_ALLOW_INLINE_COMMENTS
+ int was_space = 0;
+ while (*s && (!chars || !strchr(chars, *s)) &&
+ !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
+ was_space = isspace((unsigned char)(*s));
+ s++;
+ }
+#else
+ while (*s && (!chars || !strchr(chars, *s))) {
+ s++;
+ }
+#endif
+ return (char*)s;
+}
+
+/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
+static char* strncpy0(char* dest, const char* src, size_t size)
+{
+ strncpy(dest, src, size - 1);
+ dest[size - 1] = '\0';
+ return dest;
+}
+
+/* See documentation in header file. */
+int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
+ void* user)
+{
+ /* Uses a fair bit of stack (use heap instead if you need to) */
+#if INI_USE_STACK
+ char line[INI_MAX_LINE];
+ int max_line = INI_MAX_LINE;
+#else
+ char* line;
+ int max_line = INI_INITIAL_ALLOC;
+#endif
+#if INI_ALLOW_REALLOC
+ char* new_line;
+ int offset;
+#endif
+ char section[MAX_SECTION] = "";
+ char prev_name[MAX_NAME] = "";
+
+ char* start;
+ char* end;
+ char* name;
+ char* value;
+ int lineno = 0;
+ int error = 0;
+
+#if !INI_USE_STACK
+ line = (char*)malloc(INI_INITIAL_ALLOC);
+ if (!line) {
+ return -2;
+ }
+#endif
+
+#if INI_HANDLER_LINENO
+#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
+#else
+#define HANDLER(u, s, n, v) handler(u, s, n, v)
+#endif
+
+ /* Scan through stream line by line */
+ while (reader(line, max_line, stream) != NULL) {
+#if INI_ALLOW_REALLOC
+ offset = strlen(line);
+ while (offset == max_line - 1 && line[offset - 1] != '\n') {
+ max_line *= 2;
+ if (max_line > INI_MAX_LINE)
+ max_line = INI_MAX_LINE;
+ new_line = realloc(line, max_line);
+ if (!new_line) {
+ free(line);
+ return -2;
+ }
+ line = new_line;
+ if (reader(line + offset, max_line - offset, stream) == NULL)
+ break;
+ if (max_line >= INI_MAX_LINE)
+ break;
+ offset += strlen(line + offset);
+ }
+#endif
+
+ lineno++;
+
+ start = line;
+#if INI_ALLOW_BOM
+ if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
+ (unsigned char)start[1] == 0xBB &&
+ (unsigned char)start[2] == 0xBF) {
+ start += 3;
+ }
+#endif
+ start = lskip(rstrip(start));
+
+ if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
+ /* Start-of-line comment */
+ }
+#if INI_ALLOW_MULTILINE
+ else if (*prev_name && *start && start > line) {
+ /* Non-blank line with leading whitespace, treat as continuation
+ of previous name's value (as per Python configparser). */
+ if (!HANDLER(user, section, prev_name, start) && !error)
+ error = lineno;
+ }
+#endif
+ else if (*start == '[') {
+ /* A "[section]" line */
+ end = find_chars_or_comment(start + 1, "]");
+ if (*end == ']') {
+ *end = '\0';
+ strncpy0(section, start + 1, sizeof(section));
+ *prev_name = '\0';
+ }
+ else if (!error) {
+ /* No ']' found on section line */
+ error = lineno;
+ }
+ }
+ else if (*start) {
+ /* Not a comment, must be a name[=:]value pair */
+ end = find_chars_or_comment(start, "=:");
+ if (*end == '=' || *end == ':') {
+ *end = '\0';
+ name = rstrip(start);
+ value = end + 1;
+#if INI_ALLOW_INLINE_COMMENTS
+ end = find_chars_or_comment(value, NULL);
+ if (*end)
+ *end = '\0';
+#endif
+ value = lskip(value);
+ rstrip(value);
+
+ /* Valid name[=:]value pair found, call handler */
+ strncpy0(prev_name, name, sizeof(prev_name));
+ if (!HANDLER(user, section, name, value) && !error)
+ error = lineno;
+ }
+ else if (!error) {
+ /* No '=' or ':' found on name[=:]value line */
+ error = lineno;
+ }
+ }
+
+#if INI_STOP_ON_FIRST_ERROR
+ if (error)
+ break;
+#endif
+ }
+
+#if !INI_USE_STACK
+ free(line);
+#endif
+
+ return error;
+}
+
+/* See documentation in header file. */
+int ini_parse_file(FILE* file, ini_handler handler, void* user)
+{
+ return ini_parse_stream((ini_reader)fgets, file, handler, user);
+}
+
+/* See documentation in header file. */
+int ini_parse(const char* filename, ini_handler handler, void* user)
+{
+ FILE* file;
+ int error;
+
+ file = fopen(filename, "r");
+ if (!file)
+ return -1;
+ error = ini_parse_file(file, handler, user);
+ fclose(file);
+ return error;
+}
+
+/* An ini_reader function to read the next line from a string buffer. This
+ is the fgets() equivalent used by ini_parse_string(). */
+static char* ini_reader_string(char* str, int num, void* stream) {
+ ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream;
+ const char* ctx_ptr = ctx->ptr;
+ size_t ctx_num_left = ctx->num_left;
+ char* strp = str;
+ char c;
+
+ if (ctx_num_left == 0 || num < 2)
+ return NULL;
+
+ while (num > 1 && ctx_num_left != 0) {
+ c = *ctx_ptr++;
+ ctx_num_left--;
+ *strp++ = c;
+ if (c == '\n')
+ break;
+ num--;
+ }
+
+ *strp = '\0';
+ ctx->ptr = ctx_ptr;
+ ctx->num_left = ctx_num_left;
+ return str;
+}
+
+/* See documentation in header file. */
+int ini_parse_string(const char* string, ini_handler handler, void* user) {
+ ini_parse_string_ctx ctx;
+
+ ctx.ptr = string;
+ ctx.num_left = strlen(string);
+ return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
+ user);
+}
diff --git a/stratosphere/fs_mitm/source/ini.h b/stratosphere/fs_mitm/source/ini.h
new file mode 100644
index 000000000..f45ba40ba
--- /dev/null
+++ b/stratosphere/fs_mitm/source/ini.h
@@ -0,0 +1,130 @@
+/* inih -- simple .INI file parser
+
+inih is released under the New BSD license (see LICENSE.txt). Go to the project
+home page for more info:
+
+https://github.com/benhoyt/inih
+
+*/
+
+#ifndef __INI_H__
+#define __INI_H__
+
+/* Make this header file easier to include in C++ code */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+/* Nonzero if ini_handler callback should accept lineno parameter. */
+#ifndef INI_HANDLER_LINENO
+#define INI_HANDLER_LINENO 0
+#endif
+
+/* Typedef for prototype of handler function. */
+#if INI_HANDLER_LINENO
+typedef int (*ini_handler)(void* user, const char* section,
+ const char* name, const char* value,
+ int lineno);
+#else
+typedef int (*ini_handler)(void* user, const char* section,
+ const char* name, const char* value);
+#endif
+
+/* Typedef for prototype of fgets-style reader function. */
+typedef char* (*ini_reader)(char* str, int num, void* stream);
+
+/* Parse given INI-style file. May have [section]s, name=value pairs
+ (whitespace stripped), and comments starting with ';' (semicolon). Section
+ is "" if name=value pair parsed before any section heading. name:value
+ pairs are also supported as a concession to Python's configparser.
+
+ For each name=value pair parsed, call handler function with given user
+ pointer as well as section, name, and value (data only valid for duration
+ of handler call). Handler should return nonzero on success, zero on error.
+
+ Returns 0 on success, line number of first error on parse error (doesn't
+ stop on first error), -1 on file open error, or -2 on memory allocation
+ error (only when INI_USE_STACK is zero).
+*/
+int ini_parse(const char* filename, ini_handler handler, void* user);
+
+/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
+ close the file when it's finished -- the caller must do that. */
+int ini_parse_file(FILE* file, ini_handler handler, void* user);
+
+/* Same as ini_parse(), but takes an ini_reader function pointer instead of
+ filename. Used for implementing custom or string-based I/O (see also
+ ini_parse_string). */
+int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
+ void* user);
+
+/* Same as ini_parse(), but takes a zero-terminated string with the INI data
+instead of a file. Useful for parsing INI data from a network socket or
+already in memory. */
+int ini_parse_string(const char* string, ini_handler handler, void* user);
+
+/* Nonzero to allow multi-line value parsing, in the style of Python's
+ configparser. If allowed, ini_parse() will call the handler with the same
+ name for each subsequent line parsed. */
+#ifndef INI_ALLOW_MULTILINE
+#define INI_ALLOW_MULTILINE 1
+#endif
+
+/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
+ the file. See http://code.google.com/p/inih/issues/detail?id=21 */
+#ifndef INI_ALLOW_BOM
+#define INI_ALLOW_BOM 1
+#endif
+
+/* Chars that begin a start-of-line comment. Per Python configparser, allow
+ both ; and # comments at the start of a line by default. */
+#ifndef INI_START_COMMENT_PREFIXES
+#define INI_START_COMMENT_PREFIXES ";#"
+#endif
+
+/* Nonzero to allow inline comments (with valid inline comment characters
+ specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
+ Python 3.2+ configparser behaviour. */
+#ifndef INI_ALLOW_INLINE_COMMENTS
+#define INI_ALLOW_INLINE_COMMENTS 1
+#endif
+#ifndef INI_INLINE_COMMENT_PREFIXES
+#define INI_INLINE_COMMENT_PREFIXES ";"
+#endif
+
+/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */
+#ifndef INI_USE_STACK
+#define INI_USE_STACK 1
+#endif
+
+/* Maximum line length for any line in INI file (stack or heap). Note that
+ this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */
+#ifndef INI_MAX_LINE
+#define INI_MAX_LINE 200
+#endif
+
+/* Nonzero to allow heap line buffer to grow via realloc(), zero for a
+ fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is
+ zero. */
+#ifndef INI_ALLOW_REALLOC
+#define INI_ALLOW_REALLOC 0
+#endif
+
+/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK
+ is zero. */
+#ifndef INI_INITIAL_ALLOC
+#define INI_INITIAL_ALLOC 200
+#endif
+
+/* Stop parsing on first error (default is to keep parsing). */
+#ifndef INI_STOP_ON_FIRST_ERROR
+#define INI_STOP_ON_FIRST_ERROR 0
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INI_H__ */
diff --git a/stratosphere/fs_mitm/source/mitm_query_service.hpp b/stratosphere/fs_mitm/source/mitm_query_service.hpp
deleted file mode 100644
index a1153c496..000000000
--- a/stratosphere/fs_mitm/source/mitm_query_service.hpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2018 Atmosphère-NX
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#pragma once
-#include
-#include
-
-#include "debug.hpp"
-
-enum MitMQueryServiceCommand {
- MQS_Cmd_ShouldMitm = 65000,
- MQS_Cmd_AssociatePidTid = 65001
-};
-
-namespace MitMQueryUtils {
- Result get_associated_tid_for_pid(u64 pid, u64 *tid);
-
- void associate_pid_to_tid(u64 pid, u64 tid);
-}
-
-
-template
-class MitMQueryService : public IServiceObject {
- public:
- MitMQueryService *clone() override {
- return new MitMQueryService();
- }
- Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) override {
- Log(armGetTls(), 0x100);
- switch (cmd_id) {
- case MQS_Cmd_ShouldMitm:
- return WrapIpcCommandImpl<&MitMQueryService::should_mitm>(this, r, out_c, pointer_buffer, pointer_buffer_size);
- case MQS_Cmd_AssociatePidTid:
- return WrapIpcCommandImpl<&MitMQueryService::associate_pid_tid>(this, r, out_c, pointer_buffer, pointer_buffer_size);
- default:
- return 0xF601;
- }
- if (cmd_id == 65000) {
-
- } else {
- return 0xF601;
- }
- }
- Result handle_deferred() override {
- /* This service is never deferrable. */
- return 0;
- }
-
- protected:
- std::tuple should_mitm(u64 pid) {
- u64 should_mitm = 0;
- u64 tid = 0;
- if (R_SUCCEEDED(MitMQueryUtils::get_associated_tid_for_pid(pid, &tid))) {
- should_mitm = T::should_mitm(pid, tid);
- }
- return {0, should_mitm};
- }
- std::tuple associate_pid_tid(u64 pid, u64 tid) {
- MitMQueryUtils::associate_pid_to_tid(pid, tid);
- return {0x0};
- }
-};
\ No newline at end of file
diff --git a/stratosphere/fs_mitm/source/mitm_server.hpp b/stratosphere/fs_mitm/source/mitm_server.hpp
deleted file mode 100644
index 4054dd9ac..000000000
--- a/stratosphere/fs_mitm/source/mitm_server.hpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (c) 2018 Atmosphère-NX
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#pragma once
-#include
-#include
-
-#include "mitm_query_service.hpp"
-#include "sm_mitm.h"
-#include "mitm_session.hpp"
-
-#include "debug.hpp"
-
-template
-class MitMSession;
-
-template
-class MitMServer final : public IServer {
- static_assert(std::is_base_of::value, "Service Objects must derive from IServiceObject");
- private:
- char mitm_name[9];
-
- public:
- MitMServer(ISession> **out_query_session, const char *service_name, unsigned int max_s, bool s_d = false) : IServer(service_name, max_s, s_d) {
- Handle tmp_hnd;
- Handle out_query_h;
- Result rc;
-
- if (R_SUCCEEDED((rc = smGetServiceOriginal(&tmp_hnd, smEncodeName(service_name))))) {
- svcCloseHandle(tmp_hnd);
- } else {
- fatalSimple(rc);
- }
- strncpy(mitm_name, service_name, 8);
- mitm_name[8] = '\x00';
- if (R_FAILED((rc = smMitMInstall(&this->port_handle, &out_query_h, mitm_name)))) {
- fatalSimple(rc);
- }
- *out_query_session = new ServiceSession>(NULL, out_query_h, 0);
- }
-
- virtual ~MitMServer() {
- if (this->port_handle) {
- if (R_FAILED(smMitMUninstall(this->mitm_name))) {
- /* TODO: Panic. */
- }
- /* svcCloseHandle(port_handle); was called by ~IServer. */
- }
- }
-
- ISession *get_new_session(Handle session_h) override {
- return new MitMSession(this, session_h, 0, mitm_name);
- }
-};
-
-
diff --git a/stratosphere/fs_mitm/source/mitm_session.hpp b/stratosphere/fs_mitm/source/mitm_session.hpp
deleted file mode 100644
index 0d535b529..000000000
--- a/stratosphere/fs_mitm/source/mitm_session.hpp
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright (c) 2018 Atmosphère-NX
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#pragma once
-#include
-#include
-#include "imitmserviceobject.hpp"
-
-#include "mitm_query_service.hpp"
-#include "mitm_server.hpp"
-#include "fsmitm_worker.hpp"
-
-#include "debug.hpp"
-
-template
-class MitMServer;
-
-template
-class MitMSession final : public ISession {
- static_assert(std::is_base_of::value, "MitM Service Objects must derive from IMitMServiceObject");
-
- /* This will be for the actual session. */
- Service forward_service;
- IpcParsedCommand cur_out_r;
- u32 mitm_domain_id = 0;
- bool got_first_message;
-
- public:
- MitMSession(MitMServer *s, Handle s_h, Handle c_h, const char *srv) : ISession(s, s_h, c_h, NULL, 0), got_first_message(false) {
- this->server = s;
- this->server_handle = s_h;
- this->client_handle = c_h;
- if (R_FAILED(smMitMGetService(&forward_service, srv))) {
- /* TODO: Panic. */
- }
- size_t pointer_buffer_size = 0;
- if (R_FAILED(ipcQueryPointerBufferSize(forward_service.handle, &pointer_buffer_size))) {
- /* TODO: Panic. */
- }
- this->service_object = std::make_shared(&forward_service);
- this->pointer_buffer.resize(pointer_buffer_size);
- }
- MitMSession(MitMServer *s, Handle s_h, Handle c_h, Handle f_h) : ISession(s, s_h, c_h, NULL, 0), got_first_message(true) {
- this->server = s;
- this->server_handle = s_h;
- this->client_handle = c_h;
- serviceCreate(&this->forward_service, f_h);
- size_t pointer_buffer_size = 0;
- if (R_FAILED(ipcQueryPointerBufferSize(forward_service.handle, &pointer_buffer_size))) {
- /* TODO: Panic. */
- }
- this->service_object = std::make_shared(&forward_service);
- this->pointer_buffer.resize(pointer_buffer_size);
- }
-
- virtual ~MitMSession() {
- serviceClose(&forward_service);
- }
-
- Result handle_message(IpcParsedCommand &r) override {
- IpcCommand c;
- ipcInitialize(&c);
- u64 cmd_id = ((u32 *)r.Raw)[2];
- Result retval = 0xF601;
-
- cur_out_r.NumHandles = 0;
-
- Log(armGetTls(), 0x100);
-
- u32 *cmdbuf = (u32 *)armGetTls();
- if (r.CommandType == IpcCommandType_Close) {
- Reboot();
- }
-
- if (r.CommandType == IpcCommandType_Request || r.CommandType == IpcCommandType_RequestWithContext) {
- std::shared_ptr obj;
- if (r.IsDomainMessage) {
- obj = this->domain->get_domain_object(r.ThisObjectId);
- if (obj != nullptr && r.MessageType == DomainMessageType_Close) {
- if (r.ThisObjectId == this->mitm_domain_id) {
- Reboot();
- }
- this->domain->delete_object(r.ThisObjectId);
- struct {
- u64 magic;
- u64 result;
- } *o_resp;
-
- o_resp = (decltype(o_resp)) ipcPrepareHeaderForDomain(&c, sizeof(*o_resp), 0);
- *(DomainMessageHeader *)((uintptr_t)o_resp - sizeof(DomainMessageHeader)) = {0};
- o_resp->magic = SFCO_MAGIC;
- o_resp->result = 0x0;
- Log(armGetTls(), 0x100);
- return o_resp->result;
- }
- } else {
- obj = this->service_object;
- }
- if (obj != nullptr) {
- retval = obj->dispatch(r, c, cmd_id, (u8 *)this->pointer_buffer.data(), this->pointer_buffer.size());
- if (R_SUCCEEDED(retval)) {
- if (r.IsDomainMessage) {
- ipcParseForDomain(&cur_out_r);
- } else {
- ipcParse(&cur_out_r);
- }
- return retval;
- }
- }
- } else if (r.CommandType == IpcCommandType_Control || r.CommandType == IpcCommandType_ControlWithContext) {
- /* Ipc Clone Current Object. */
- retval = serviceIpcDispatch(&forward_service);
- Log(armGetTls(), 0x100);
- if (R_SUCCEEDED(retval)) {
- ipcParse(&cur_out_r);
- struct {
- u64 magic;
- u64 result;
- } *resp = (decltype(resp))cur_out_r.Raw;
- retval = resp->result;
- if ((cmd_id == IpcCtrl_Cmd_CloneCurrentObject || cmd_id == IpcCtrl_Cmd_CloneCurrentObjectEx)) {
- if (R_SUCCEEDED(retval)) {
- Handle s_h;
- Handle c_h;
- Result rc;
- if (R_FAILED((rc = svcCreateSession(&s_h, &c_h, 0, 0)))) {
- fatalSimple(rc);
- }
-
- if (cur_out_r.NumHandles != 1) {
- Reboot();
- }
-
- MitMSession *new_sess = new MitMSession((MitMServer *)this->server, s_h, c_h, cur_out_r.Handles[0]);
- new_sess->service_object = this->service_object;
-
- if (this->is_domain) {
- new_sess->is_domain = true;
- new_sess->domain = this->domain;
- new_sess->mitm_domain_id = this->mitm_domain_id;
- new_sess->forward_service.type = this->forward_service.type;
- new_sess->forward_service.object_id = this->forward_service.object_id;
- }
- this->get_manager()->add_waitable(new_sess);
- ipcSendHandleMove(&c, c_h);
- struct {
- u64 magic;
- u64 result;
- } *o_resp;
-
- o_resp = (decltype(o_resp)) ipcPrepareHeader(&c, sizeof(*o_resp));
- o_resp->magic = SFCO_MAGIC;
- o_resp->result = 0x0;
- }
- }
- }
- Log(armGetTls(), 0x100);
- return retval;
- }
-
- /* 0xF601 --> Dispatch onwards. */
- if (retval == 0xF601) {
- /* Patch PID Descriptor, if relevant. */
- if (r.HasPid) {
- /* [ctrl 0] [ctrl 1] [handle desc 0] [pid low] [pid high] */
- cmdbuf[4] = 0xFFFE0000UL | (cmdbuf[4] & 0xFFFFUL);
- }
-
- Log(armGetTls(), 0x100);
- retval = serviceIpcDispatch(&forward_service);
- if (R_SUCCEEDED(retval)) {
- if (r.IsDomainMessage) {
- ipcParseForDomain(&cur_out_r);
- } else {
- ipcParse(&cur_out_r);
- }
-
- struct {
- u64 magic;
- u64 result;
- } *resp = (decltype(resp))cur_out_r.Raw;
-
- retval = resp->result;
- }
- }
-
- Log(armGetTls(), 0x100);
- Log(&cmd_id, sizeof(u64));
- u64 retval_for_log = retval;
- Log(&retval_for_log, sizeof(u64));
- if (R_FAILED(retval)) {
- //Reboot();
- }
-
- return retval;
- }
-
- void postprocess(IpcParsedCommand &r, u64 cmd_id) override {
- if (this->active_object == this->service_object && (r.CommandType == IpcCommandType_Request || r.CommandType == IpcCommandType_RequestWithContext)) {
- IpcCommand c;
- ipcInitialize(&c);
- this->service_object->postprocess(cur_out_r, c, cmd_id, (u8 *)this->pointer_buffer.data(), this->pointer_buffer.size());
- } else if (r.CommandType == IpcCommandType_Control || r.CommandType == IpcCommandType_ControlWithContext) {
- if (cmd_id == IpcCtrl_Cmd_ConvertCurrentObjectToDomain) {
- this->is_domain = true;
- this->domain = std::make_shared();
- struct {
- u64 magic;
- u64 result;
- u32 domain_id;
- } *resp = (decltype(resp))cur_out_r.Raw;
- Result rc;
- if (R_FAILED((rc = this->domain->set_object(this->service_object, resp->domain_id)))) {
- fatalSimple(rc);
- }
- this->mitm_domain_id = resp->domain_id;
- this->forward_service.type = ServiceType_Domain;
- this->forward_service.object_id = resp->domain_id;
- }
- }
- }
-
- void cleanup() override {
- /* Clean up copy handles. */
- for (unsigned int i = 0; i < cur_out_r.NumHandles; i++) {
- if (cur_out_r.WasHandleCopied[i]) {
- svcCloseHandle(cur_out_r.Handles[i]);
- }
- }
- }
-};
diff --git a/stratosphere/libstratosphere/Makefile b/stratosphere/libstratosphere/Makefile
index ff0e010e7..70ba1949b 100644
--- a/stratosphere/libstratosphere/Makefile
+++ b/stratosphere/libstratosphere/Makefile
@@ -18,7 +18,7 @@ include $(DEVKITPRO)/libnx/switch_rules
TARGET := $(notdir $(CURDIR))
SOURCES := source
DATA := data
-INCLUDES := include
+INCLUDES := include ../../common/include
#---------------------------------------------------------------------------------
# options for code generation
diff --git a/stratosphere/libstratosphere/include/boost/callable_traits.hpp b/stratosphere/libstratosphere/include/boost/callable_traits.hpp
deleted file mode 100644
index 87f0fa622..000000000
--- a/stratosphere/libstratosphere/include/boost/callable_traits.hpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
-
-@Copyright Barrett Adair 2015-2017
-Distributed under the Boost Software License, Version 1.0.
-(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
-
-*/
-
-#ifndef BOOST_CLBL_TRTS_BOOST_CLBL_TRTS_HPP
-#define BOOST_CLBL_TRTS_BOOST_CLBL_TRTS_HPP
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#endif
diff --git a/stratosphere/libstratosphere/include/boost/callable_traits/add_member_const.hpp b/stratosphere/libstratosphere/include/boost/callable_traits/add_member_const.hpp
deleted file mode 100644
index cd7280ef8..000000000
--- a/stratosphere/libstratosphere/include/boost/callable_traits/add_member_const.hpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
-
-@Copyright Barrett Adair 2015-2017
-Distributed under the Boost Software License, Version 1.0.
-(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
-
-*/
-
-#ifndef BOOST_CLBL_TRTS_ADD_MEMBER_CONST_HPP
-#define BOOST_CLBL_TRTS_ADD_MEMBER_CONST_HPP
-
-#include
-
-namespace boost { namespace callable_traits {
-
-//[ add_member_const_hpp
-/*`
-[section:ref_add_member_const add_member_const]
-[heading Header]
-``#include ``
-[heading Definition]
-*/
-
-template
-using add_member_const_t = //see below
-//<-
-#ifdef BOOST_CLBL_TRTS_DISABLE_ABOMINABLE_FUNCTIONS
-
- detail::sfinae_try<
- typename detail::traits::add_member_const,
-
- detail::fail_when_same::add_member_const,
- detail::abominable_functions_not_supported_on_this_compiler,
- this_compiler_doesnt_support_abominable_function_types>,
-
- detail::fail_if_invalid::add_member_const,
- member_qualifiers_are_illegal_for_this_type>>;
-#else
-
- detail::try_but_fail_if_invalid<
- typename detail::traits::add_member_const,
- member_qualifiers_are_illegal_for_this_type>;
-
-#endif // #ifdef BOOST_CLBL_TRTS_DISABLE_ABOMINABLE_FUNCTIONS
-
-namespace detail {
-
- template
- struct add_member_const_impl {};
-
- template
- struct add_member_const_impl , detail::dummy>::type>
- {
- using type = add_member_const_t;
- };
-}
-
-//->
-
-template
-struct add_member_const : detail::add_member_const_impl {};
-
-//<-
-}} // namespace boost::callable_traits
-//->
-
-
-/*`
-[heading Constraints]
-* `T` must be a function type or a member function pointer type
-* If `T` is a pointer, it may not be cv/ref qualified
-
-[heading Behavior]
-* A substitution failure occurs if the constraints are violated.
-* Adds a member `const` qualifier to `T`, if not already present.
-
-[heading Input/Output Examples]
-[table
- [[`T`] [`add_member_const_t`]]
- [[`int()`] [`int() const`]]
- [[`int(foo::*)()`] [`int(foo::*)() const`]]
- [[`int(foo::*)() &`] [`int(foo::*)() const &`]]
- [[`int(foo::*)() &&`] [`int(foo::*)() const &&`]]
- [[`int(foo::*)() const`] [`int(foo::*)() const`]]
- [[`int(foo::*)() volatile`] [`int(foo::*)() const volatile`]]
- [[`int(foo::*)() transaction_safe`] [`int(foo::*)() const transaction_safe`]]
- [[`int`] [(substitution failure)]]
- [[`int (&)()`] [(substitution failure)]]
- [[`int (*)()`] [(substitution failure)]]
- [[`int foo::*`] [(substitution failure)]]
- [[`int (foo::* const)()`] [(substitution failure)]]
-]
-
-[heading Example Program]
-[import ../example/add_member_const.cpp]
-[add_member_const]
-[endsect]
-*/
-//]
-
-#endif // #ifndef BOOST_CLBL_TRTS_ADD_MEMBER_CONST_HPP
-
-
-
diff --git a/stratosphere/libstratosphere/include/boost/callable_traits/add_member_cv.hpp b/stratosphere/libstratosphere/include/boost/callable_traits/add_member_cv.hpp
deleted file mode 100644
index e11cc0754..000000000
--- a/stratosphere/libstratosphere/include/boost/callable_traits/add_member_cv.hpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
-
-@Copyright Barrett Adair 2015-2017
-Distributed under the Boost Software License, Version 1.0.
-(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
-
-*/
-
-#ifndef BOOST_CLBL_TRTS_ADD_MEMBER_CV_HPP
-#define BOOST_CLBL_TRTS_ADD_MEMBER_CV_HPP
-
-#include
-
-namespace boost { namespace callable_traits {
-
-//[ add_member_cv_hpp
-/*`
-[section:ref_add_member_cv add_member_cv]
-[heading Header]
-``#include ``
-[heading Definition]
-*/
-
-template
-using add_member_cv_t = //see below
-//<-
-#ifdef BOOST_CLBL_TRTS_DISABLE_ABOMINABLE_FUNCTIONS
-
- detail::sfinae_try<
- typename detail::traits::add_member_cv,
-
- detail::fail_when_same::add_member_cv,
- detail::abominable_functions_not_supported_on_this_compiler,
- this_compiler_doesnt_support_abominable_function_types>,
-
- detail::fail_if_invalid::add_member_cv,
- member_qualifiers_are_illegal_for_this_type>>;
-#else
-
- detail::try_but_fail_if_invalid<
- typename detail::traits::add_member_cv,
- member_qualifiers_are_illegal_for_this_type>;
-
-#endif // #ifdef BOOST_CLBL_TRTS_DISABLE_ABOMINABLE_FUNCTIONS
-
-namespace detail {
-
- template
- struct add_member_cv_impl {};
-
- template
- struct add_member_cv_impl , detail::dummy>::type>
- {
- using type = add_member_cv_t;
- };
-}
-
-//->
-
-template
-struct add_member_cv : detail::add_member_cv_impl {};
-
-//<-
-}} // namespace boost::callable_traits
-//->
-
-/*`
-[heading Constraints]
-* `T` must be a function type or a member function pointer type
-* If `T` is a pointer, it may not be cv/ref qualified
-
-[heading Behavior]
-* A substitution failure occurs if the constraints are violated.
-* Adds member `const` and `volatile` qualifiers to `T`, if not already present.
-
-[heading Input/Output Examples]
-[table
- [[`T`] [`add_member_cv_t`]]
- [[`int()`] [`int() const volatile`]]
- [[`int(foo::*)()`] [`int(foo::*)() const volatile`]]
- [[`int(foo::*)() &`] [`int(foo::*)() const volatile &`]]
- [[`int(foo::*)() &&`] [`int(foo::*)() const volatile &&`]]
- [[`int(foo::*)() const`] [`int(foo::*)() const volatile`]]
- [[`int(foo::*)() volatile`] [`int(foo::*)() const volatile`]]
- [[`int(foo::*)() transaction_safe`] [`int(foo::*)() const volatile transaction_safe`]]
- [[`int`] [(substitution failure)]]
- [[`int (&)()`] [(substitution failure)]]
- [[`int (*)()`] [(substitution failure)]]
- [[`int foo::*`] [(substitution failure)]]
- [[`int (foo::* const)()`] [(substitution failure)]]
-]
-
-[heading Example Program]
-[import ../example/add_member_cv.cpp]
-[add_member_cv]
-[endsect]
-*/
-//]
-
-#endif
diff --git a/stratosphere/libstratosphere/include/boost/callable_traits/add_member_lvalue_reference.hpp b/stratosphere/libstratosphere/include/boost/callable_traits/add_member_lvalue_reference.hpp
deleted file mode 100644
index e23d71a00..000000000
--- a/stratosphere/libstratosphere/include/boost/callable_traits/add_member_lvalue_reference.hpp
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
-
-@Copyright Barrett Adair 2015-2017
-Distributed under the Boost Software License, Version 1.0.
-(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
-
-*/
-
-#ifndef BOOST_CLBL_TRTS_ADD_MEMBER_LVALUE_REFERENCE_HPP
-#define BOOST_CLBL_TRTS_ADD_MEMBER_LVALUE_REFERENCE_HPP
-
-#include
-
-namespace boost { namespace callable_traits {
-
-//[ add_member_lvalue_reference_hpp
-/*`
-[section:ref_add_member_lvalue_reference add_member_lvalue_reference]
-[heading Header]
-``#include ``
-[heading Definition]
-*/
-
-#ifdef BOOST_CLBL_TRTS_DISABLE_REFERENCE_QUALIFIERS
-
-template
-struct add_member_lvalue_reference_t {
- static_assert(std::is_same::value,
- "Reference member qualifiers are not supported by this configuration.");
-};
-
-#else
-
-template
-using add_member_lvalue_reference_t = //see below
-//<-
-#ifdef BOOST_CLBL_TRTS_DISABLE_ABOMINABLE_FUNCTIONS
-
- detail::sfinae_try<
- typename detail::traits::add_member_lvalue_reference,
-
- detail::fail_when_same::add_member_lvalue_reference,
- detail::abominable_functions_not_supported_on_this_compiler,
- this_compiler_doesnt_support_abominable_function_types>,
-
- detail::fail_if_invalid<
- typename detail::traits::add_member_lvalue_reference,
- member_qualifiers_are_illegal_for_this_type>>;
-#else
-
- detail::try_but_fail_if_invalid<
- typename detail::traits::add_member_lvalue_reference,
- member_qualifiers_are_illegal_for_this_type>;
-
-#endif // #ifdef BOOST_CLBL_TRTS_DISABLE_ABOMINABLE_FUNCTIONS
-#endif // #ifdef BOOST_CLBL_TRTS_DISABLE_REFERENCE_QUALIFIERS
-
-namespace detail {
-
- template
- struct add_member_lvalue_reference_impl {};
-
- template
- struct add_member_lvalue_reference_impl , detail::dummy>::type>
- {
- using type = add_member_lvalue_reference_t;
- };
-}
-//->
-
-template
-struct add_member_lvalue_reference
- : detail::add_member_lvalue_reference_impl {};
-
-//<-
-}} // namespace boost::callable_traits
-//->
-
-/*`
-[heading Constraints]
-* `T` must be a function type or a member function pointer type
-* If `T` is a pointer, it may not be cv/ref qualified
-
-[heading Behavior]
-* A substitution failure occurs if the constraints are violated.
-* Adds a member lvalue reference qualifier (`&`) to `T`, if not already present.
-* If an rvalue reference qualifier is present, the lvalue reference qualifier replaces it (in accordance with reference collapsing rules).
-
-[heading Input/Output Examples]
-[table
- [[`T`] [`add_member_lvalue_reference_t`]]
- [[`int()`] [`int() &`]]
- [[`int(foo::*)()`] [`int(foo::*)() &`]]
- [[`int(foo::*)() &`] [`int(foo::*)() &`]]
- [[`int(foo::*)() &&`] [`int(foo::*)() &`]]
- [[`int(foo::*)() const`] [`int(foo::*)() const &`]]
- [[`int(foo::*)() transaction_safe`] [`int(foo::*)() & transaction_safe`]]
- [[`int`] [(substitution failure)]]
- [[`int (&)()`] [(substitution failure)]]
- [[`int (*)()`] [(substitution failure)]]
- [[`int foo::*`] [(substitution failure)]]
- [[`int (foo::* const)()`] [(substitution failure)]]
-]
-
-[heading Example Program]
-[import ../example/add_member_lvalue_reference.cpp]
-[add_member_lvalue_reference]
-[endsect]
-*/
-//]
-
-#endif
-
diff --git a/stratosphere/libstratosphere/include/boost/callable_traits/add_member_rvalue_reference.hpp b/stratosphere/libstratosphere/include/boost/callable_traits/add_member_rvalue_reference.hpp
deleted file mode 100644
index 84e3c5eaf..000000000
--- a/stratosphere/libstratosphere/include/boost/callable_traits/add_member_rvalue_reference.hpp
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
-
-@Copyright Barrett Adair 2015-2017
-Distributed under the Boost Software License, Version 1.0.
-(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
-
-*/
-
-#ifndef BOOST_CLBL_TRTS_ADD_MEMBER_RVALUE_REFERENCE_HPP
-#define BOOST_CLBL_TRTS_ADD_MEMBER_RVALUE_REFERENCE_HPP
-
-#include
-
-namespace boost { namespace callable_traits {
-
-//[ add_member_rvalue_reference_hpp
-/*`
-[section:ref_add_member_rvalue_reference add_member_rvalue_reference]
-[heading Header]
-``#include ``
-[heading Definition]
-*/
-
-#ifdef BOOST_CLBL_TRTS_DISABLE_REFERENCE_QUALIFIERS
-
-template
-struct add_member_rvalue_reference_t {
- static_assert(std::is_same