mirror of
				https://github.com/Atmosphere-NX/Atmosphere.git
				synced 2025-11-04 12:51:17 +01:00 
			
		
		
		
	dmnt: pull in from ams.tma branch
This commit is contained in:
		
						commit
						89503049b3
					
				
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							@ -54,6 +54,7 @@ dist: all
 | 
			
		||||
	mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034
 | 
			
		||||
	mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032
 | 
			
		||||
	cp fusee/fusee-primary/fusee-primary.bin atmosphere-$(AMSVER)/atmosphere/reboot_payload.bin
 | 
			
		||||
	mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D
 | 
			
		||||
	cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/atmosphere/fusee-secondary.bin
 | 
			
		||||
	cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/sept/payload.bin
 | 
			
		||||
	cp sept/sept-primary/sept-primary.bin atmosphere-$(AMSVER)/sept/sept-primary.bin
 | 
			
		||||
@ -69,6 +70,7 @@ dist: all
 | 
			
		||||
	cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro
 | 
			
		||||
	mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags
 | 
			
		||||
	touch atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags/boot2.flag
 | 
			
		||||
	cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D/exefs.nsp
 | 
			
		||||
	cd atmosphere-$(AMSVER); zip -r ../atmosphere-$(AMSVER).zip ./*; cd ../;
 | 
			
		||||
	rm -r atmosphere-$(AMSVER)
 | 
			
		||||
	mkdir out
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
MODULES := loader pm sm boot ams_mitm eclct.stub creport fatal
 | 
			
		||||
MODULES := loader pm sm boot ams_mitm eclct.stub creport fatal dmnt
 | 
			
		||||
 | 
			
		||||
SUBFOLDERS := libstratosphere $(MODULES)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										166
									
								
								stratosphere/dmnt/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								stratosphere/dmnt/Makefile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,166 @@
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
.SUFFIXES:
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
ifeq ($(strip $(DEVKITPRO)),)
 | 
			
		||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
TOPDIR ?= $(CURDIR)
 | 
			
		||||
include $(DEVKITPRO)/libnx/switch_rules
 | 
			
		||||
 | 
			
		||||
AMSBRANCH := $(shell git symbolic-ref --short HEAD)
 | 
			
		||||
AMSREV := $(AMSBRANCH)-$(shell git rev-parse --short HEAD)
 | 
			
		||||
 | 
			
		||||
ifneq (, $(strip $(shell git status --porcelain 2>/dev/null)))
 | 
			
		||||
    AMSREV := $(AMSREV)-dirty
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
# TARGET is the name of the output
 | 
			
		||||
# BUILD is the directory where object files & intermediate files will be placed
 | 
			
		||||
# SOURCES is a list of directories containing source code
 | 
			
		||||
# DATA is a list of directories containing data files
 | 
			
		||||
# INCLUDES is a list of directories containing header files
 | 
			
		||||
# EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm".
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
TARGET		:=	$(notdir $(CURDIR))
 | 
			
		||||
BUILD		:=	build
 | 
			
		||||
SOURCES		:=	source
 | 
			
		||||
DATA		:=	data
 | 
			
		||||
INCLUDES	:=	include ../../common/include
 | 
			
		||||
EXEFS_SRC	:=	exefs_src
 | 
			
		||||
 | 
			
		||||
DEFINES	:=	-DDISABLE_IPC -DATMOSPHERE_GIT_BRANCH=\"$(AMSBRANCH)\" -DATMOSPHERE_GIT_REV=\"$(AMSREV)\"
 | 
			
		||||
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
# options for code generation
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
ARCH	:=	-march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
 | 
			
		||||
 | 
			
		||||
CFLAGS	:=	-g -Wall -O2 -ffunction-sections \
 | 
			
		||||
			$(ARCH) $(DEFINES)
 | 
			
		||||
 | 
			
		||||
CFLAGS	+=	$(INCLUDE) -D__SWITCH__
 | 
			
		||||
 | 
			
		||||
CXXFLAGS	:= $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
 | 
			
		||||
 | 
			
		||||
ASFLAGS	:=	-g $(ARCH)
 | 
			
		||||
LDFLAGS	=	-specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
 | 
			
		||||
 | 
			
		||||
LIBS	:= -lstratosphere -lnx
 | 
			
		||||
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
# list of directories containing libraries, this must be the top level containing
 | 
			
		||||
# include and lib
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
LIBDIRS	:= $(PORTLIBS) $(LIBNX) $(CURDIR)/../libstratosphere
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
# no real need to edit anything past this point unless you need to add additional
 | 
			
		||||
# rules for different file extensions
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
export OUTPUT	:=	$(CURDIR)/$(TARGET)
 | 
			
		||||
export TOPDIR	:=	$(CURDIR)
 | 
			
		||||
 | 
			
		||||
export VPATH	:=	$(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
 | 
			
		||||
			$(foreach dir,$(DATA),$(CURDIR)/$(dir))
 | 
			
		||||
 | 
			
		||||
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)))
 | 
			
		||||
BINFILES	:=	$(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
 | 
			
		||||
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
# use CXX for linking C++ projects, CC for standard C
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
ifeq ($(strip $(CPPFILES)),)
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
	export LD	:=	$(CC)
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
else
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
	export LD	:=	$(CXX)
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
endif
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
export OFILES	:=	$(addsuffix .o,$(BINFILES)) \
 | 
			
		||||
			$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
 | 
			
		||||
 | 
			
		||||
export INCLUDE	:=	$(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
 | 
			
		||||
			$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
 | 
			
		||||
			-I$(CURDIR)/$(BUILD)
 | 
			
		||||
 | 
			
		||||
export LIBPATHS	:=	$(foreach dir,$(LIBDIRS),-L$(dir)/lib)
 | 
			
		||||
 | 
			
		||||
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
 | 
			
		||||
 | 
			
		||||
ifeq ($(strip $(CONFIG_JSON)),)
 | 
			
		||||
	jsons := $(wildcard *.json)
 | 
			
		||||
	ifneq (,$(findstring $(TARGET).json,$(jsons)))
 | 
			
		||||
		export APP_JSON := $(TOPDIR)/$(TARGET).json
 | 
			
		||||
	else
 | 
			
		||||
		ifneq (,$(findstring config.json,$(jsons)))
 | 
			
		||||
			export APP_JSON := $(TOPDIR)/config.json
 | 
			
		||||
		endif
 | 
			
		||||
	endif
 | 
			
		||||
else
 | 
			
		||||
	export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
.PHONY: $(BUILD) clean all
 | 
			
		||||
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
all: $(BUILD)
 | 
			
		||||
 | 
			
		||||
$(BUILD):
 | 
			
		||||
	@[ -d $@ ] || mkdir -p $@
 | 
			
		||||
	@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
 | 
			
		||||
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
clean:
 | 
			
		||||
	@echo clean ...
 | 
			
		||||
	@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).npdm $(TARGET).nso $(TARGET).elf
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
else
 | 
			
		||||
.PHONY:	all
 | 
			
		||||
 | 
			
		||||
DEPENDS	:=	$(OFILES:.o=.d)
 | 
			
		||||
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
# main targets
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
all	:	$(OUTPUT).nsp
 | 
			
		||||
 | 
			
		||||
ifeq ($(strip $(APP_JSON)),)
 | 
			
		||||
$(OUTPUT).nsp	:	$(OUTPUT).nso
 | 
			
		||||
else
 | 
			
		||||
$(OUTPUT).nsp	:	$(OUTPUT).nso $(OUTPUT).npdm
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
$(OUTPUT).nso	:	$(OUTPUT).elf
 | 
			
		||||
 | 
			
		||||
$(OUTPUT).elf	:	$(OFILES)
 | 
			
		||||
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
# you need a rule like this for each extension you use as binary data
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
%.bin.o	:	%.bin
 | 
			
		||||
#---------------------------------------------------------------------------------
 | 
			
		||||
	@echo $(notdir $<)
 | 
			
		||||
	@$(bin2o)
 | 
			
		||||
 | 
			
		||||
-include $(DEPENDS)
 | 
			
		||||
 | 
			
		||||
#---------------------------------------------------------------------------------------
 | 
			
		||||
endif
 | 
			
		||||
#---------------------------------------------------------------------------------------
 | 
			
		||||
							
								
								
									
										117
									
								
								stratosphere/dmnt/dmnt.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								stratosphere/dmnt/dmnt.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,117 @@
 | 
			
		||||
{
 | 
			
		||||
	"name":	"dmnt",
 | 
			
		||||
	"title_id":	"0x010000000000000d",
 | 
			
		||||
	"title_id_range_min":	"0x010000000000000d",
 | 
			
		||||
	"title_id_range_max":	"0x010000000000000d",
 | 
			
		||||
	"main_thread_stack_size":	"0x00004000",
 | 
			
		||||
	"main_thread_priority":	39,
 | 
			
		||||
	"default_cpu_id":	3,
 | 
			
		||||
	"process_category":	0,
 | 
			
		||||
	"is_retail":	true,
 | 
			
		||||
	"pool_partition":	2,
 | 
			
		||||
	"is_64_bit":	true,
 | 
			
		||||
	"address_space_type":	1,
 | 
			
		||||
	"filesystem_access":	{
 | 
			
		||||
		"permissions":	"0xFFFFFFFFFFFFFFFF"
 | 
			
		||||
	},
 | 
			
		||||
	"service_access":	[
 | 
			
		||||
        "pm:dmnt",
 | 
			
		||||
        "ldr:dmnt",
 | 
			
		||||
        "ro:dmnt",
 | 
			
		||||
        "ns:dev",
 | 
			
		||||
        "spl:",
 | 
			
		||||
        "lr",
 | 
			
		||||
        "htc",
 | 
			
		||||
        "bsd:s",
 | 
			
		||||
        "sfdnsres",
 | 
			
		||||
        "bsdcfg",
 | 
			
		||||
        "set",
 | 
			
		||||
        "fsp-srv",
 | 
			
		||||
        "fatal:u"
 | 
			
		||||
    ],
 | 
			
		||||
	"service_host":	[
 | 
			
		||||
        "dmnt:-"
 | 
			
		||||
    ],
 | 
			
		||||
	"kernel_capabilities":	[{
 | 
			
		||||
			"type":	"kernel_flags",
 | 
			
		||||
			"value":	{
 | 
			
		||||
				"highest_thread_priority":	63,
 | 
			
		||||
				"lowest_thread_priority":	24,
 | 
			
		||||
				"lowest_cpu_id":	0,
 | 
			
		||||
				"highest_cpu_id":	3
 | 
			
		||||
			}
 | 
			
		||||
		}, {
 | 
			
		||||
			"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",
 | 
			
		||||
				"svcDebugActiveProcess":	"0x60",
 | 
			
		||||
				"svcBreakDebugProcess":	"0x61",
 | 
			
		||||
				"svcTerminateDebugProcess":	"0x62",
 | 
			
		||||
				"svcGetDebugEvent":	"0x63",
 | 
			
		||||
				"svcContinueDebugEvent":	"0x64",
 | 
			
		||||
				"svcGetProcessList":	"0x65",
 | 
			
		||||
				"svcGetThreadList":	"0x66",
 | 
			
		||||
				"svcGetDebugThreadContext":	"0x67",
 | 
			
		||||
				"svcSetDebugThreadContext":	"0x68",
 | 
			
		||||
				"svcQueryDebugProcessMemory":	"0x69",
 | 
			
		||||
				"svcReadDebugProcessMemory":	"0x6a",
 | 
			
		||||
				"svcWriteDebugProcessMemory":	"0x6b",
 | 
			
		||||
				"svcSetHardwareBreakPoint":	"0x6c",
 | 
			
		||||
				"svcGetDebugThreadParam":	"0x6d"
 | 
			
		||||
			}
 | 
			
		||||
		}, {
 | 
			
		||||
			"type":	"min_kernel_version",
 | 
			
		||||
			"value":	"0x0030"
 | 
			
		||||
		}, {
 | 
			
		||||
			"type":	"handle_table_size",
 | 
			
		||||
			"value":	0
 | 
			
		||||
		}]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										140
									
								
								stratosphere/dmnt/source/dmnt_main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								stratosphere/dmnt/source/dmnt_main.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,140 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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 <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <malloc.h>
 | 
			
		||||
 | 
			
		||||
#include <switch.h>
 | 
			
		||||
#include <atmosphere.h>
 | 
			
		||||
#include <stratosphere.hpp>
 | 
			
		||||
 | 
			
		||||
#include "dmnt_service.hpp"
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
    extern u32 __start__;
 | 
			
		||||
 | 
			
		||||
    u32 __nx_applet_type = AppletType_None;
 | 
			
		||||
 | 
			
		||||
    #define INNER_HEAP_SIZE 0x80000
 | 
			
		||||
    size_t nx_inner_heap_size = INNER_HEAP_SIZE;
 | 
			
		||||
    char   nx_inner_heap[INNER_HEAP_SIZE];
 | 
			
		||||
    
 | 
			
		||||
    void __libnx_initheap(void);
 | 
			
		||||
    void __appInit(void);
 | 
			
		||||
    void __appExit(void);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void __libnx_initheap(void) {
 | 
			
		||||
	void*  addr = nx_inner_heap;
 | 
			
		||||
	size_t size = nx_inner_heap_size;
 | 
			
		||||
 | 
			
		||||
	/* Newlib */
 | 
			
		||||
	extern char* fake_heap_start;
 | 
			
		||||
	extern char* fake_heap_end;
 | 
			
		||||
 | 
			
		||||
	fake_heap_start = (char*)addr;
 | 
			
		||||
	fake_heap_end   = (char*)addr + size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __appInit(void) {
 | 
			
		||||
    Result rc;
 | 
			
		||||
    
 | 
			
		||||
    rc = smInitialize();
 | 
			
		||||
    if (R_FAILED(rc)) {
 | 
			
		||||
        fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_SM));
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    rc = pmdmntInitialize();
 | 
			
		||||
    if (R_FAILED(rc)) {
 | 
			
		||||
        fatalSimple(rc);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    rc = ldrDmntInitialize();
 | 
			
		||||
    if (R_FAILED(rc)) {
 | 
			
		||||
        fatalSimple(rc);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /*
 | 
			
		||||
    if (kernelAbove300()) {
 | 
			
		||||
        rc = roDmntInitialize();
 | 
			
		||||
        if (R_FAILED(rc)) {
 | 
			
		||||
            fatalSimple(rc);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
    
 | 
			
		||||
    rc = nsdevInitialize();
 | 
			
		||||
    if (R_FAILED(rc)) {
 | 
			
		||||
        fatalSimple(rc);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    rc = lrInitialize();
 | 
			
		||||
    if (R_FAILED(rc)) {
 | 
			
		||||
        fatalSimple(rc);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    rc = setInitialize();
 | 
			
		||||
    if (R_FAILED(rc)) {
 | 
			
		||||
        fatalSimple(rc);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    rc = fsInitialize();
 | 
			
		||||
    if (R_FAILED(rc)) {
 | 
			
		||||
        fatalSimple(rc);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    rc = fsdevMountSdmc();
 | 
			
		||||
    if (R_FAILED(rc)) {
 | 
			
		||||
        fatalSimple(rc);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __appExit(void) {
 | 
			
		||||
    /* Cleanup services. */
 | 
			
		||||
    fsdevUnmountAll();
 | 
			
		||||
    fsExit();
 | 
			
		||||
    setExit();
 | 
			
		||||
    lrExit();
 | 
			
		||||
    nsdevExit();
 | 
			
		||||
    /* if (kernelAbove300()) { roDmntExit(); } */
 | 
			
		||||
    ldrDmntExit();
 | 
			
		||||
    pmdmntExit();
 | 
			
		||||
    smExit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
    consoleDebugInit(debugDevice_SVC);
 | 
			
		||||
    
 | 
			
		||||
    /* Nintendo uses four threads. */
 | 
			
		||||
    auto server_manager = new WaitableManager(4);
 | 
			
		||||
    
 | 
			
		||||
    /* Create services. */
 | 
			
		||||
    server_manager->AddWaitable(new ServiceServer<DebugMonitorService>("dmnt:-", 4));
 | 
			
		||||
 | 
			
		||||
    /* Loop forever, servicing our services. */
 | 
			
		||||
    server_manager->Process();
 | 
			
		||||
 | 
			
		||||
    delete server_manager;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										150
									
								
								stratosphere/dmnt/source/dmnt_service.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								stratosphere/dmnt/source/dmnt_service.hpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,150 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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 <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <switch.h>
 | 
			
		||||
#include <stratosphere.hpp>
 | 
			
		||||
 | 
			
		||||
enum DmntCmd {
 | 
			
		||||
    DebugMonitor_Cmd_BreakDebugProcess = 0,
 | 
			
		||||
    DebugMonitor_Cmd_TerminateDebugProcess = 1,
 | 
			
		||||
    DebugMonitor_Cmd_CloseHandle = 2,
 | 
			
		||||
    DebugMonitor_Cmd_LoadImage = 3,
 | 
			
		||||
    DebugMonitor_Cmd_GetProcessId = 4,
 | 
			
		||||
    DebugMonitor_Cmd_GetProcessHandle = 5,
 | 
			
		||||
    DebugMonitor_Cmd_WaitSynchronization = 6,
 | 
			
		||||
    DebugMonitor_Cmd_GetDebugEvent = 7,
 | 
			
		||||
    DebugMonitor_Cmd_GetProcessModuleInfo = 8,
 | 
			
		||||
    DebugMonitor_Cmd_GetProcessList = 9,
 | 
			
		||||
    DebugMonitor_Cmd_GetThreadList = 10,
 | 
			
		||||
    DebugMonitor_Cmd_GetDebugThreadContext = 11,
 | 
			
		||||
    DebugMonitor_Cmd_ContinueDebugEvent = 12,
 | 
			
		||||
    DebugMonitor_Cmd_ReadDebugProcessMemory = 13,
 | 
			
		||||
    DebugMonitor_Cmd_WriteDebugProcessMemory = 14,
 | 
			
		||||
    DebugMonitor_Cmd_SetDebugThreadContext = 15,
 | 
			
		||||
    DebugMonitor_Cmd_GetDebugThreadParam = 16,
 | 
			
		||||
    DebugMonitor_Cmd_InitializeThreadInfo = 17,
 | 
			
		||||
    DebugMonitor_Cmd_SetHardwareBreakPoint = 18,
 | 
			
		||||
    DebugMonitor_Cmd_QueryDebugProcessMemory = 19,
 | 
			
		||||
    DebugMonitor_Cmd_GetProcessMemoryDetails = 20,
 | 
			
		||||
    DebugMonitor_Cmd_AttachByProgramId = 21,
 | 
			
		||||
    DebugMonitor_Cmd_AttachOnLaunch = 22,
 | 
			
		||||
    DebugMonitor_Cmd_GetDebugMonitorProcessId = 23,
 | 
			
		||||
    DebugMonitor_Cmd_GetJitDebugProcessList = 25,
 | 
			
		||||
    DebugMonitor_Cmd_CreateCoreDump = 26,
 | 
			
		||||
    DebugMonitor_Cmd_GetAllDebugThreadInfo = 27,
 | 
			
		||||
    DebugMonitor_Cmd_TargetIO_FileOpen = 29,
 | 
			
		||||
    DebugMonitor_Cmd_TargetIO_FileClose = 30,
 | 
			
		||||
    DebugMonitor_Cmd_TargetIO_FileRead = 31,
 | 
			
		||||
    DebugMonitor_Cmd_TargetIO_FileWrite = 32,
 | 
			
		||||
    DebugMonitor_Cmd_TargetIO_FileSetAttributes = 33,
 | 
			
		||||
    DebugMonitor_Cmd_TargetIO_FileGetInformation = 34,
 | 
			
		||||
    DebugMonitor_Cmd_TargetIO_FileSetTime = 35,
 | 
			
		||||
    DebugMonitor_Cmd_TargetIO_FileSetSize = 36,
 | 
			
		||||
    DebugMonitor_Cmd_TargetIO_FileDelete = 37,
 | 
			
		||||
    DebugMonitor_Cmd_TargetIO_FileMove = 38,
 | 
			
		||||
    DebugMonitor_Cmd_TargetIO_DirectoryCreate = 39,
 | 
			
		||||
    DebugMonitor_Cmd_TargetIO_DirectoryDelete = 40,
 | 
			
		||||
    DebugMonitor_Cmd_TargetIO_DirectoryRename = 41,
 | 
			
		||||
    DebugMonitor_Cmd_TargetIO_DirectoryGetCount = 42,
 | 
			
		||||
    DebugMonitor_Cmd_TargetIO_DirectoryOpen = 43,
 | 
			
		||||
    DebugMonitor_Cmd_TargetIO_DirectoryGetNext = 44,
 | 
			
		||||
    DebugMonitor_Cmd_TargetIO_DirectoryClose = 45,
 | 
			
		||||
    DebugMonitor_Cmd_TargetIO_GetFreeSpace = 46,
 | 
			
		||||
    DebugMonitor_Cmd_TargetIO_GetVolumeInformation = 47,
 | 
			
		||||
    DebugMonitor_Cmd_InitiateCoreDump = 48,
 | 
			
		||||
    DebugMonitor_Cmd_ContinueCoreDump = 49,
 | 
			
		||||
    DebugMonitor_Cmd_AddTTYToCoreDump = 50,
 | 
			
		||||
    DebugMonitor_Cmd_AddImageToCoreDump = 51,
 | 
			
		||||
    DebugMonitor_Cmd_CloseCoreDump = 52,
 | 
			
		||||
    DebugMonitor_Cmd_CancelAttach = 53,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class DebugMonitorService final : public IServiceObject {
 | 
			
		||||
    private:
 | 
			
		||||
        Result BreakDebugProcess(Handle debug_hnd);
 | 
			
		||||
        Result TerminateDebugProcess(Handle debug_hnd);
 | 
			
		||||
        Result CloseHandle(Handle debug_hnd);
 | 
			
		||||
        Result GetProcessId(Out<u64> out_pid, Handle hnd);
 | 
			
		||||
        Result GetProcessHandle(Out<Handle> out_hnd, u64 pid);
 | 
			
		||||
        Result WaitSynchronization(Handle hnd, u64 ns);
 | 
			
		||||
        
 | 
			
		||||
        Result TargetIO_FileOpen(OutBuffer<u64> out_hnd, InBuffer<char> path, int open_mode, u32 create_mode);
 | 
			
		||||
        Result TargetIO_FileClose(InBuffer<u64> hnd);
 | 
			
		||||
        Result TargetIO_FileRead(InBuffer<u64> hnd, OutBuffer<u8, BufferType_Type1> out_data, Out<u32> out_read, u64 offset);
 | 
			
		||||
        Result TargetIO_FileWrite(InBuffer<u64> hnd, InBuffer<u8, BufferType_Type1> data, Out<u32> out_written, u64 offset);
 | 
			
		||||
        Result TargetIO_FileSetAttributes(InBuffer<char> path, InBuffer<u8> attributes);
 | 
			
		||||
        Result TargetIO_FileGetInformation(InBuffer<char> path, OutBuffer<u64> out_info, Out<int> is_directory);
 | 
			
		||||
        Result TargetIO_FileSetTime(InBuffer<char> path, u64 create, u64 access, u64 modify);
 | 
			
		||||
        Result TargetIO_FileSetSize(InBuffer<char> path, u64 size);
 | 
			
		||||
        Result TargetIO_FileDelete(InBuffer<char> path);
 | 
			
		||||
        Result TargetIO_FileMove(InBuffer<char> path0, InBuffer<char> path1);
 | 
			
		||||
    public:
 | 
			
		||||
        DEFINE_SERVICE_DISPATCH_TABLE {
 | 
			
		||||
            MakeServiceCommandMeta<DebugMonitor_Cmd_BreakDebugProcess, &DebugMonitorService::BreakDebugProcess>(),
 | 
			
		||||
            MakeServiceCommandMeta<DebugMonitor_Cmd_TerminateDebugProcess, &DebugMonitorService::TerminateDebugProcess>(),
 | 
			
		||||
            MakeServiceCommandMeta<DebugMonitor_Cmd_CloseHandle, &DebugMonitorService::CloseHandle>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_LoadImage, &DebugMonitorService::LoadImage>(),
 | 
			
		||||
            MakeServiceCommandMeta<DebugMonitor_Cmd_GetProcessId, &DebugMonitorService::GetProcessId>(),
 | 
			
		||||
            MakeServiceCommandMeta<DebugMonitor_Cmd_GetProcessHandle, &DebugMonitorService::GetProcessHandle>(),
 | 
			
		||||
            MakeServiceCommandMeta<DebugMonitor_Cmd_WaitSynchronization, &DebugMonitorService::WaitSynchronization>(),
 | 
			
		||||
            //MakeServiceCommandMeta<DebugMonitor_Cmd_GetDebugEvent, &DebugMonitorService::GetDebugEvent>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_GetProcessModuleInfo, &DebugMonitorService::GetProcessModuleInfo>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_GetProcessList, &DebugMonitorService::GetProcessList>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_GetThreadList, &DebugMonitorService::GetThreadList>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_GetDebugThreadContext, &DebugMonitorService::GetDebugThreadContext>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_ContinueDebugEvent, &DebugMonitorService::ContinueDebugEvent>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_ReadDebugProcessMemory, &DebugMonitorService::ReadDebugProcessMemory>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_WriteDebugProcessMemory, &DebugMonitorService::WriteDebugProcessMemory>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_SetDebugThreadContext, &DebugMonitorService::SetDebugThreadContext>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_GetDebugThreadParam, &DebugMonitorService::GetDebugThreadParam>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_InitializeThreadInfo, &DebugMonitorService::InitializeThreadInfo>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_SetHardwareBreakPoint, &DebugMonitorService::SetHardwareBreakPoint>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_QueryDebugProcessMemory, &DebugMonitorService::QueryDebugProcessMemory>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_GetProcessMemoryDetails, &DebugMonitorService::GetProcessMemoryDetails>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_AttachByProgramId, &DebugMonitorService::AttachByProgramId>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_AttachOnLaunch, &DebugMonitorService::AttachOnLaunch>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_GetDebugMonitorProcessId, &DebugMonitorService::GetDebugMonitorProcessId>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_GetJitDebugProcessList, &DebugMonitorService::GetJitDebugProcessList>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_CreateCoreDump, &DebugMonitorService::CreateCoreDump>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_GetAllDebugThreadInfo, &DebugMonitorService::GetAllDebugThreadInfo>(),
 | 
			
		||||
            MakeServiceCommandMeta<DebugMonitor_Cmd_TargetIO_FileOpen, &DebugMonitorService::TargetIO_FileOpen>(),
 | 
			
		||||
            MakeServiceCommandMeta<DebugMonitor_Cmd_TargetIO_FileClose, &DebugMonitorService::TargetIO_FileClose>(),
 | 
			
		||||
            MakeServiceCommandMeta<DebugMonitor_Cmd_TargetIO_FileRead, &DebugMonitorService::TargetIO_FileRead>(),
 | 
			
		||||
            MakeServiceCommandMeta<DebugMonitor_Cmd_TargetIO_FileWrite, &DebugMonitorService::TargetIO_FileWrite>(),
 | 
			
		||||
            MakeServiceCommandMeta<DebugMonitor_Cmd_TargetIO_FileSetAttributes, &DebugMonitorService::TargetIO_FileSetAttributes>(),
 | 
			
		||||
            MakeServiceCommandMeta<DebugMonitor_Cmd_TargetIO_FileGetInformation, &DebugMonitorService::TargetIO_FileGetInformation>(),
 | 
			
		||||
            MakeServiceCommandMeta<DebugMonitor_Cmd_TargetIO_FileSetTime, &DebugMonitorService::TargetIO_FileSetTime>(),
 | 
			
		||||
            MakeServiceCommandMeta<DebugMonitor_Cmd_TargetIO_FileSetSize, &DebugMonitorService::TargetIO_FileSetSize>(),
 | 
			
		||||
            MakeServiceCommandMeta<DebugMonitor_Cmd_TargetIO_FileDelete, &DebugMonitorService::TargetIO_FileDelete>(),
 | 
			
		||||
            MakeServiceCommandMeta<DebugMonitor_Cmd_TargetIO_FileMove, &DebugMonitorService::TargetIO_FileMove>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_TargetIO_DirectoryCreate, &DebugMonitorService::TargetIO_DirectoryCreate>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_TargetIO_DirectoryDelete, &DebugMonitorService::TargetIO_DirectoryDelete>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_TargetIO_DirectoryRename, &DebugMonitorService::TargetIO_DirectoryRename>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_TargetIO_DirectoryGetCount, &DebugMonitorService::TargetIO_DirectoryGetCount>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_TargetIO_DirectoryOpen, &DebugMonitorService::TargetIO_DirectoryOpen>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_TargetIO_DirectoryGetNext, &DebugMonitorService::TargetIO_DirectoryGetNext>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_TargetIO_DirectoryClose, &DebugMonitorService::TargetIO_DirectoryClose>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_TargetIO_GetFreeSpace, &DebugMonitorService::TargetIO_GetFreeSpace>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_TargetIO_GetVolumeInformation, &DebugMonitorService::TargetIO_GetVolumeInformation>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_InitiateCoreDump, &DebugMonitorService::InitiateCoreDump>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_ContinueCoreDump, &DebugMonitorService::ContinueCoreDump>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_AddTTYToCoreDump, &DebugMonitorService::AddTTYToCoreDump>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_AddImageToCoreDump, &DebugMonitorService::AddImageToCoreDump>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_CloseCoreDump, &DebugMonitorService::CloseCoreDump>(),
 | 
			
		||||
            // MakeServiceCommandMeta<DebugMonitor_Cmd_CancelAttach, &DebugMonitorService::CancelAttach>(),
 | 
			
		||||
        };
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										52
									
								
								stratosphere/dmnt/source/dmnt_service_debug.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								stratosphere/dmnt/source/dmnt_service_debug.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,52 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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 <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
#include <switch.h>
 | 
			
		||||
#include "dmnt_service.hpp"
 | 
			
		||||
 | 
			
		||||
Result DebugMonitorService::BreakDebugProcess(Handle debug_hnd) {
 | 
			
		||||
    /* Nintendo discards the output of this command, but we will return it. */
 | 
			
		||||
    return svcBreakDebugProcess(debug_hnd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result DebugMonitorService::TerminateDebugProcess(Handle debug_hnd) {
 | 
			
		||||
    /* Nintendo discards the output of this command, but we will return it. */
 | 
			
		||||
    return svcTerminateDebugProcess(debug_hnd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result DebugMonitorService::CloseHandle(Handle debug_hnd) {
 | 
			
		||||
    /* Nintendo discards the output of this command, but we will return it. */
 | 
			
		||||
    /* This command is, entertainingly, also pretty unsafe in general... */
 | 
			
		||||
    return svcCloseHandle(debug_hnd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result DebugMonitorService::GetProcessId(Out<u64> out_pid, Handle hnd) {
 | 
			
		||||
    /* Nintendo discards the output of this command, but we will return it. */
 | 
			
		||||
    return svcGetProcessId(out_pid.GetPointer(), hnd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result DebugMonitorService::GetProcessHandle(Out<Handle> out_hnd, u64 pid) {
 | 
			
		||||
    Result rc = svcDebugActiveProcess(out_hnd.GetPointer(), pid);
 | 
			
		||||
    if (rc == 0xF401) {
 | 
			
		||||
        rc = 0x4B7;
 | 
			
		||||
    }
 | 
			
		||||
    return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result DebugMonitorService::WaitSynchronization(Handle hnd, u64 ns) {
 | 
			
		||||
    /* Nintendo discards the output of this command, but we will return it. */
 | 
			
		||||
    return svcWaitSynchronizationSingle(hnd, ns);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										299
									
								
								stratosphere/dmnt/source/dmnt_service_target_io.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										299
									
								
								stratosphere/dmnt/source/dmnt_service_target_io.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,299 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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 <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <switch.h>
 | 
			
		||||
#include "dmnt_service.hpp"
 | 
			
		||||
 | 
			
		||||
enum TIOCreateOption : u32 {
 | 
			
		||||
    TIOCreateOption_CreateNew = 1,
 | 
			
		||||
    TIOCreateOption_CreateAlways = 2,
 | 
			
		||||
    TIOCreateOption_OpenExisting = 3,
 | 
			
		||||
    TIOCreateOption_OpenAlways = 4,
 | 
			
		||||
    TIOCreateOption_ResetSize = 5,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Nintendo uses actual pointers as file handles. We'll add a layer of indirection... */
 | 
			
		||||
static bool g_sd_initialized = false;
 | 
			
		||||
static HosMutex g_sd_lock;
 | 
			
		||||
static FsFileSystem g_sd_fs;
 | 
			
		||||
 | 
			
		||||
static HosMutex g_file_handle_lock;
 | 
			
		||||
static u64 g_cur_fd = 0;
 | 
			
		||||
static std::unordered_map<u64, FsFile> g_file_handles;
 | 
			
		||||
 | 
			
		||||
static Result EnsureSdInitialized() {
 | 
			
		||||
    std::scoped_lock<HosMutex> lk(g_sd_lock);
 | 
			
		||||
    if (g_sd_initialized) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    Result rc = fsMountSdcard(&g_sd_fs);
 | 
			
		||||
    if (R_SUCCEEDED(rc)) {
 | 
			
		||||
        g_sd_initialized = true;
 | 
			
		||||
    }
 | 
			
		||||
    return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static u64 GetFileHandle(FsFile f) {
 | 
			
		||||
    std::scoped_lock<HosMutex> lk(g_file_handle_lock);
 | 
			
		||||
    
 | 
			
		||||
    u64 fd = g_cur_fd++;
 | 
			
		||||
    g_file_handles[fd] = f;
 | 
			
		||||
    return fd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Result GetFileByHandle(FsFile *out, u64 handle) {
 | 
			
		||||
    std::scoped_lock<HosMutex> lk(g_file_handle_lock);
 | 
			
		||||
    if (g_file_handles.find(handle) != g_file_handles.end()) {
 | 
			
		||||
        *out = g_file_handles[handle];
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    return 0x2EE202;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Result CloseFileByHandle(u64 handle) {
 | 
			
		||||
    std::scoped_lock<HosMutex> lk(g_file_handle_lock);
 | 
			
		||||
    if (g_file_handles.find(handle) != g_file_handles.end()) {
 | 
			
		||||
        fsFileClose(&g_file_handles[handle]);
 | 
			
		||||
        g_file_handles.erase(handle);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    return 0x2EE202;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void FixPath(char *dst, size_t dst_size, InBuffer<char> &path) {
 | 
			
		||||
    dst[dst_size - 1] = 0;
 | 
			
		||||
    strncpy(dst, "/", dst_size - 1);
 | 
			
		||||
    
 | 
			
		||||
    const char *src = path.buffer;
 | 
			
		||||
    size_t src_idx = 0;
 | 
			
		||||
    size_t dst_idx = 1;
 | 
			
		||||
    while (src_idx < path.num_elements && (src[src_idx] == '/' || src[src_idx] == '\\')) {
 | 
			
		||||
        src_idx++;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    while (src_idx < path.num_elements && dst_idx < dst_size - 1 && src[src_idx] != 0) {
 | 
			
		||||
        if (src[src_idx] == '\\') {
 | 
			
		||||
            dst[dst_idx] = '/';
 | 
			
		||||
        } else {
 | 
			
		||||
            dst[dst_idx] = src[src_idx];
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        src_idx++;
 | 
			
		||||
        dst_idx++;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    if (dst_idx < dst_size) {
 | 
			
		||||
        dst[dst_idx] = 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result DebugMonitorService::TargetIO_FileOpen(OutBuffer<u64> out_hnd, InBuffer<char> path, int open_mode, u32 create_mode) {
 | 
			
		||||
    if (out_hnd.num_elements != 1) {
 | 
			
		||||
        return 0xF601;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    Result rc = EnsureSdInitialized();
 | 
			
		||||
    if (R_FAILED(rc)) {
 | 
			
		||||
        return rc;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    char fs_path[FS_MAX_PATH];
 | 
			
		||||
    FixPath(fs_path, sizeof(fs_path), path);
 | 
			
		||||
    
 | 
			
		||||
    if (create_mode == TIOCreateOption_CreateAlways) {
 | 
			
		||||
        fsFsDeleteFile(&g_sd_fs, fs_path);
 | 
			
		||||
        rc = fsFsCreateFile(&g_sd_fs, fs_path, 0, 0);
 | 
			
		||||
    } else if (create_mode == TIOCreateOption_CreateNew) {
 | 
			
		||||
        rc = fsFsCreateFile(&g_sd_fs, fs_path, 0, 0);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    if (R_FAILED(rc)) {
 | 
			
		||||
        return rc;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    FsFile f;
 | 
			
		||||
    rc = fsFsOpenFile(&g_sd_fs, fs_path, open_mode, &f);
 | 
			
		||||
    if (R_FAILED(rc)) {
 | 
			
		||||
        if (create_mode == TIOCreateOption_OpenAlways) {
 | 
			
		||||
            fsFsCreateFile(&g_sd_fs, fs_path, 0, 0);
 | 
			
		||||
            rc = fsFsOpenFile(&g_sd_fs, fs_path, open_mode, &f);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    if (R_SUCCEEDED(rc)) {
 | 
			
		||||
        if (create_mode == TIOCreateOption_ResetSize) {
 | 
			
		||||
            rc = fsFileSetSize(&f, 0);
 | 
			
		||||
        }
 | 
			
		||||
        if (R_SUCCEEDED(rc)) {
 | 
			
		||||
            out_hnd[0] = GetFileHandle(f);
 | 
			
		||||
        } else {
 | 
			
		||||
            fsFileClose(&f);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result DebugMonitorService::TargetIO_FileClose(InBuffer<u64> hnd) {
 | 
			
		||||
    if (hnd.num_elements != 1) {
 | 
			
		||||
        return 0xF601;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return CloseFileByHandle(hnd[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result DebugMonitorService::TargetIO_FileRead(InBuffer<u64> hnd, OutBuffer<u8, BufferType_Type1> out_data, Out<u32> out_read, u64 offset) {
 | 
			
		||||
    if (hnd.num_elements != 1) {
 | 
			
		||||
        return 0xF601;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    FsFile f;
 | 
			
		||||
    Result rc = GetFileByHandle(&f, hnd[0]);
 | 
			
		||||
    if (R_FAILED(rc)) {
 | 
			
		||||
        return rc;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    size_t read = 0;
 | 
			
		||||
    rc = fsFileRead(&f, offset, out_data.buffer, out_data.num_elements, &read);
 | 
			
		||||
    out_read.SetValue(static_cast<u32>(read));
 | 
			
		||||
    return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result DebugMonitorService::TargetIO_FileWrite(InBuffer<u64> hnd, InBuffer<u8, BufferType_Type1> data, Out<u32> out_written, u64 offset) {
 | 
			
		||||
    if (hnd.num_elements != 1) {
 | 
			
		||||
        return 0xF601;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    FsFile f;
 | 
			
		||||
    Result rc = GetFileByHandle(&f, hnd[0]);
 | 
			
		||||
    if (R_FAILED(rc)) {
 | 
			
		||||
        return rc;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    rc = fsFileWrite(&f, offset, data.buffer, data.num_elements);
 | 
			
		||||
    if (R_SUCCEEDED(rc)) {
 | 
			
		||||
        out_written.SetValue(data.num_elements);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result DebugMonitorService::TargetIO_FileSetAttributes(InBuffer<char> path, InBuffer<u8> attributes) {
 | 
			
		||||
    /* I don't really know why this command exists, Horizon doesn't allow you to set any attributes. */
 | 
			
		||||
    /* N just returns 0x0 unconditionally here. */
 | 
			
		||||
    return 0x0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result DebugMonitorService::TargetIO_FileGetInformation(InBuffer<char> path, OutBuffer<u64> out_info, Out<int> is_directory) {
 | 
			
		||||
    if (out_info.num_elements != 4) {
 | 
			
		||||
        return 0xF601;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    Result rc = EnsureSdInitialized();
 | 
			
		||||
    if (R_FAILED(rc)) {
 | 
			
		||||
        return rc;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    char fs_path[FS_MAX_PATH];
 | 
			
		||||
    FixPath(fs_path, sizeof(fs_path), path);
 | 
			
		||||
    
 | 
			
		||||
    for (size_t i = 0; i < out_info.num_elements; i++) {
 | 
			
		||||
        out_info[i] = 0;
 | 
			
		||||
    }
 | 
			
		||||
    is_directory.SetValue(0);
 | 
			
		||||
    
 | 
			
		||||
    FsFile f;
 | 
			
		||||
    rc = fsFsOpenFile(&g_sd_fs, fs_path, FS_OPEN_READ, &f);
 | 
			
		||||
    if (R_SUCCEEDED(rc)) {
 | 
			
		||||
        ON_SCOPE_EXIT { fsFileClose(&f); };
 | 
			
		||||
        
 | 
			
		||||
        /* N doesn't check this return code. */
 | 
			
		||||
        fsFileGetSize(&f, &out_info[0]);
 | 
			
		||||
        
 | 
			
		||||
        /* TODO: N does not call fsFsGetFileTimestampRaw here, but we possibly could. */
 | 
			
		||||
    } else {
 | 
			
		||||
        FsDir dir;
 | 
			
		||||
        rc = fsFsOpenDirectory(&g_sd_fs, fs_path, FS_DIROPEN_FILE | FS_DIROPEN_DIRECTORY, &dir);
 | 
			
		||||
        if (R_SUCCEEDED(rc)) {
 | 
			
		||||
            fsDirClose(&dir);
 | 
			
		||||
            is_directory.SetValue(1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result DebugMonitorService::TargetIO_FileSetTime(InBuffer<char> path, u64 create, u64 access, u64 modify) {
 | 
			
		||||
    /* This is another function that doesn't really need to exist, because Horizon doesn't let you set anything. */
 | 
			
		||||
    return 0x0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result DebugMonitorService::TargetIO_FileSetSize(InBuffer<char> input, u64 size) {
 | 
			
		||||
    /* Why does this function take in a path and not a file handle? */
 | 
			
		||||
    
 | 
			
		||||
    /* We will try to be better than N, here. N only treats input as a path. */
 | 
			
		||||
    if (input.num_elements == sizeof(u64)) {
 | 
			
		||||
        FsFile f;
 | 
			
		||||
        if (R_SUCCEEDED(GetFileByHandle(&f, reinterpret_cast<u64 *>(input.buffer)[0]))) {
 | 
			
		||||
            return fsFileSetSize(&f, size);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    Result rc = EnsureSdInitialized();
 | 
			
		||||
    if (R_FAILED(rc)) {
 | 
			
		||||
        return rc;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    char fs_path[FS_MAX_PATH];
 | 
			
		||||
    FixPath(fs_path, sizeof(fs_path), input);
 | 
			
		||||
    
 | 
			
		||||
    FsFile f;
 | 
			
		||||
    rc = fsFsOpenFile(&g_sd_fs, fs_path, FS_OPEN_WRITE, &f);
 | 
			
		||||
    if (R_SUCCEEDED(rc)) {
 | 
			
		||||
        rc = fsFileSetSize(&f, size);
 | 
			
		||||
        fsFileClose(&f);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result DebugMonitorService::TargetIO_FileDelete(InBuffer<char> path) {
 | 
			
		||||
    Result rc = EnsureSdInitialized();
 | 
			
		||||
    if (R_FAILED(rc)) {
 | 
			
		||||
        return rc;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    char fs_path[FS_MAX_PATH];
 | 
			
		||||
    FixPath(fs_path, sizeof(fs_path), path);
 | 
			
		||||
    
 | 
			
		||||
    return fsFsDeleteFile(&g_sd_fs, fs_path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Result DebugMonitorService::TargetIO_FileMove(InBuffer<char> path0, InBuffer<char> path1) {
 | 
			
		||||
    Result rc = EnsureSdInitialized();
 | 
			
		||||
    if (R_FAILED(rc)) {
 | 
			
		||||
        return rc;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    char fs_path0[FS_MAX_PATH];
 | 
			
		||||
    char fs_path1[FS_MAX_PATH];
 | 
			
		||||
    FixPath(fs_path0, sizeof(fs_path0), path0);
 | 
			
		||||
    FixPath(fs_path1, sizeof(fs_path1), path1);
 | 
			
		||||
    
 | 
			
		||||
    return fsFsRenameFile(&g_sd_fs, fs_path0, fs_path1);
 | 
			
		||||
}
 | 
			
		||||
@ -41,6 +41,8 @@ static bool HasLaunchedTitle(Boot2KnownTitleId title_id) {
 | 
			
		||||
    return std::find(g_boot2_titles.begin(), g_boot2_titles.end(), title_id) != g_boot2_titles.end();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static std::vector<Boot2KnownTitleId> g_launched_titles;
 | 
			
		||||
 | 
			
		||||
static bool IsHexadecimal(const char *str) {
 | 
			
		||||
    while (*str) {
 | 
			
		||||
        if (isxdigit(*str)) {
 | 
			
		||||
@ -52,8 +54,20 @@ static bool IsHexadecimal(const char *str) {
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool HasLaunchedTitle(Boot2KnownTitleId title_id) {
 | 
			
		||||
    return std::find(g_launched_titles.begin(), g_launched_titles.end(), title_id) != g_launched_titles.end();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void SetLaunchedTitle(Boot2KnownTitleId title_id) {
 | 
			
		||||
    g_launched_titles.push_back(title_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ClearLaunchedTitles() {
 | 
			
		||||
    g_launched_titles.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void LaunchTitle(Boot2KnownTitleId title_id, FsStorageId storage_id, u32 launch_flags, u64 *pid) {
 | 
			
		||||
    u64 local_pid;
 | 
			
		||||
    u64 local_pid = 0;
 | 
			
		||||
    
 | 
			
		||||
    /* Don't launch a title twice during boot2. */
 | 
			
		||||
    if (HasLaunchedTitle(title_id)) {
 | 
			
		||||
@ -64,15 +78,15 @@ static void LaunchTitle(Boot2KnownTitleId title_id, FsStorageId storage_id, u32
 | 
			
		||||
    switch (rc) {
 | 
			
		||||
        case 0xCE01:
 | 
			
		||||
            /* Out of resource! */
 | 
			
		||||
            /* TODO: Panic(). */
 | 
			
		||||
            std::abort();
 | 
			
		||||
            break;
 | 
			
		||||
        case 0xDE01:
 | 
			
		||||
            /* Out of memory! */
 | 
			
		||||
            /* TODO: Panic(). */
 | 
			
		||||
            std::abort();
 | 
			
		||||
            break;
 | 
			
		||||
        case 0xD001:
 | 
			
		||||
            /* Limit Reached! */
 | 
			
		||||
            /* TODO: Panic(). */
 | 
			
		||||
            std::abort();
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            /* We don't care about other issues. */
 | 
			
		||||
@ -235,8 +249,9 @@ void EmbeddedBoot2::Main() {
 | 
			
		||||
    
 | 
			
		||||
    /* Launch usb. */
 | 
			
		||||
    LaunchTitle(Boot2KnownTitleId::usb, FsStorageId_NandSystem, 0, NULL);
 | 
			
		||||
    /* Launch tma. */
 | 
			
		||||
    LaunchTitle(Boot2KnownTitleId::tma, FsStorageId_NandSystem, 0, NULL);
 | 
			
		||||
      
 | 
			
		||||
    /* Launch Atmosphere dmnt, using FsStorageId_None to force SD card boot. */
 | 
			
		||||
    LaunchTitle(Boot2KnownTitleId::dmnt, FsStorageId_None, 0, NULL);
 | 
			
		||||
    
 | 
			
		||||
    /* Launch default programs. */
 | 
			
		||||
    for (auto &launch_program : g_additional_launch_programs) {
 | 
			
		||||
@ -251,7 +266,10 @@ void EmbeddedBoot2::Main() {
 | 
			
		||||
    if (titles_dir != NULL) {
 | 
			
		||||
        while ((ent = readdir(titles_dir)) != NULL) {
 | 
			
		||||
            if (strlen(ent->d_name) == 0x10 && IsHexadecimal(ent->d_name)) {
 | 
			
		||||
                u64 title_id = strtoul(ent->d_name, NULL, 16);
 | 
			
		||||
                Boot2KnownTitleId title_id = (Boot2KnownTitleId)strtoul(ent->d_name, NULL, 16);
 | 
			
		||||
                if (HasLaunchedTitle(title_id)) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                char title_path[FS_MAX_PATH] = {0};
 | 
			
		||||
                strcpy(title_path, "sdmc:/atmosphere/titles/");
 | 
			
		||||
                strcat(title_path, ent->d_name);
 | 
			
		||||
@ -259,7 +277,7 @@ void EmbeddedBoot2::Main() {
 | 
			
		||||
                FILE *f_flag = fopen(title_path, "rb");
 | 
			
		||||
                if (f_flag != NULL) {
 | 
			
		||||
                    fclose(f_flag);
 | 
			
		||||
                    LaunchTitle((Boot2KnownTitleId)title_id, FsStorageId_None, 0, NULL);
 | 
			
		||||
                    LaunchTitle(title_id, FsStorageId_None, 0, NULL);
 | 
			
		||||
                } else {
 | 
			
		||||
                    /* TODO: Deprecate this in the future. */
 | 
			
		||||
                    memset(title_path, 0, FS_MAX_PATH);
 | 
			
		||||
@ -269,13 +287,16 @@ void EmbeddedBoot2::Main() {
 | 
			
		||||
                    f_flag = fopen(title_path, "rb");
 | 
			
		||||
                    if (f_flag != NULL) {
 | 
			
		||||
                        fclose(f_flag);
 | 
			
		||||
                        LaunchTitle((Boot2KnownTitleId)title_id, FsStorageId_None, 0, NULL);
 | 
			
		||||
                        LaunchTitle(title_id, FsStorageId_None, 0, NULL);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        closedir(titles_dir);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /* Free the memory used to track what boot2 launches. */
 | 
			
		||||
    ClearLaunchedTitles();
 | 
			
		||||
        
 | 
			
		||||
    /* We no longer need the SD card. */
 | 
			
		||||
    fsdevUnmountAll();
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user