├── .gitignore ├── Makefile ├── nfpu_mitm.json └── source ├── nfp_user_interface.cpp ├── nfp_user_interface.hpp ├── nfpmitm_main.cpp ├── nfpuser_mitm_service.cpp └── nfpuser_mitm_service.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | *.elf 3 | *.npdm 4 | *.nso 5 | *.nsp -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITPRO)),) 6 | $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") 7 | endif 8 | 9 | TOPDIR ?= $(CURDIR) 10 | include $(DEVKITPRO)/libnx/switch_rules 11 | 12 | AMSBRANCH := $(shell git symbolic-ref --short HEAD) 13 | AMSREV := $(AMSBRANCH)-$(shell git rev-parse --short HEAD) 14 | 15 | ifneq (, $(strip $(shell git status --porcelain 2>/dev/null))) 16 | AMSREV := $(AMSREV)-dirty 17 | endif 18 | 19 | #--------------------------------------------------------------------------------- 20 | # TARGET is the name of the output 21 | # BUILD is the directory where object files & intermediate files will be placed 22 | # SOURCES is a list of directories containing source code 23 | # DATA is a list of directories containing data files 24 | # INCLUDES is a list of directories containing header files 25 | # EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm". 26 | #--------------------------------------------------------------------------------- 27 | TARGET := $(notdir $(CURDIR)) 28 | BUILD := build 29 | SOURCES := source 30 | DATA := data 31 | INCLUDES := include ../../common/include 32 | EXEFS_SRC := exefs_src 33 | 34 | DEFINES := -DDISABLE_IPC -DATMOSPHERE_GIT_BRANCH=\"$(AMSBRANCH)\" -DATMOSPHERE_GIT_REV=\"$(AMSREV)\" 35 | 36 | #--------------------------------------------------------------------------------- 37 | # options for code generation 38 | #--------------------------------------------------------------------------------- 39 | ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE 40 | 41 | CFLAGS := -g -Wall -O2 -ffunction-sections \ 42 | $(ARCH) $(DEFINES) 43 | 44 | CFLAGS += $(INCLUDE) -D__SWITCH__ 45 | 46 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17 47 | 48 | ASFLAGS := -g $(ARCH) 49 | LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) 50 | 51 | LIBS := -lstratosphere -lnx 52 | 53 | #--------------------------------------------------------------------------------- 54 | # list of directories containing libraries, this must be the top level containing 55 | # include and lib 56 | #--------------------------------------------------------------------------------- 57 | LIBDIRS := $(PORTLIBS) $(LIBNX) $(CURDIR)/../libstratosphere 58 | 59 | 60 | #--------------------------------------------------------------------------------- 61 | # no real need to edit anything past this point unless you need to add additional 62 | # rules for different file extensions 63 | #--------------------------------------------------------------------------------- 64 | ifneq ($(BUILD),$(notdir $(CURDIR))) 65 | #--------------------------------------------------------------------------------- 66 | 67 | export OUTPUT := $(CURDIR)/$(TARGET) 68 | export TOPDIR := $(CURDIR) 69 | 70 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 71 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 72 | 73 | export DEPSDIR := $(CURDIR)/$(BUILD) 74 | 75 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 76 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 77 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 78 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 79 | 80 | #--------------------------------------------------------------------------------- 81 | # use CXX for linking C++ projects, CC for standard C 82 | #--------------------------------------------------------------------------------- 83 | ifeq ($(strip $(CPPFILES)),) 84 | #--------------------------------------------------------------------------------- 85 | export LD := $(CC) 86 | #--------------------------------------------------------------------------------- 87 | else 88 | #--------------------------------------------------------------------------------- 89 | export LD := $(CXX) 90 | #--------------------------------------------------------------------------------- 91 | endif 92 | #--------------------------------------------------------------------------------- 93 | 94 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 95 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 96 | 97 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 98 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 99 | -I$(CURDIR)/$(BUILD) 100 | 101 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 102 | 103 | export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) 104 | 105 | ifeq ($(strip $(CONFIG_JSON)),) 106 | jsons := $(wildcard *.json) 107 | ifneq (,$(findstring $(TARGET).json,$(jsons))) 108 | export APP_JSON := $(TOPDIR)/$(TARGET).json 109 | else 110 | ifneq (,$(findstring config.json,$(jsons))) 111 | export APP_JSON := $(TOPDIR)/config.json 112 | endif 113 | endif 114 | else 115 | export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) 116 | endif 117 | 118 | .PHONY: $(BUILD) clean all 119 | 120 | #--------------------------------------------------------------------------------- 121 | all: $(BUILD) 122 | 123 | $(BUILD): 124 | @[ -d $@ ] || mkdir -p $@ 125 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 126 | 127 | #--------------------------------------------------------------------------------- 128 | clean: 129 | @echo clean ... 130 | @rm -fr $(BUILD) $(TARGET).nsp $(TARGET).npdm $(TARGET).nso $(TARGET).elf 131 | 132 | 133 | #--------------------------------------------------------------------------------- 134 | else 135 | .PHONY: all 136 | 137 | DEPENDS := $(OFILES:.o=.d) 138 | 139 | #--------------------------------------------------------------------------------- 140 | # main targets 141 | #--------------------------------------------------------------------------------- 142 | all : $(OUTPUT).nsp 143 | 144 | ifeq ($(strip $(APP_JSON)),) 145 | $(OUTPUT).nsp : $(OUTPUT).nso 146 | else 147 | $(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm 148 | endif 149 | 150 | $(OUTPUT).nso : $(OUTPUT).elf 151 | 152 | $(OUTPUT).elf : $(OFILES) 153 | 154 | #--------------------------------------------------------------------------------- 155 | # you need a rule like this for each extension you use as binary data 156 | #--------------------------------------------------------------------------------- 157 | %.bin.o : %.bin 158 | #--------------------------------------------------------------------------------- 159 | @echo $(notdir $<) 160 | @$(bin2o) 161 | 162 | -include $(DEPENDS) 163 | 164 | #--------------------------------------------------------------------------------------- 165 | endif 166 | #--------------------------------------------------------------------------------------- 167 | -------------------------------------------------------------------------------- /nfpu_mitm.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nfpu.mitm", 3 | "title_id": "0x0100000000000052", 4 | "title_id_range_min": "0x0100000000000052", 5 | "title_id_range_max": "0x0100000000000052", 6 | "main_thread_stack_size": "0x00004000", 7 | "main_thread_priority": 49, 8 | "default_cpu_id": 3, 9 | "process_category": 0, 10 | "is_retail": true, 11 | "pool_partition": 2, 12 | "is_64_bit": true, 13 | "address_space_type": 1, 14 | "filesystem_access": { 15 | "permissions": "0xFFFFFFFFFFFFFFFF" 16 | }, 17 | "service_host": [ 18 | "nfp:user" 19 | ], 20 | "service_access": [ 21 | "nfp:user", 22 | "fatal:u", 23 | "fsp-srv", 24 | "spl:", 25 | "set:sys", 26 | "nvdrv:s", 27 | "hid" 28 | ], 29 | "kernel_capabilities": [ 30 | { 31 | "type": "kernel_flags", 32 | "value": { 33 | "highest_thread_priority": 63, 34 | "lowest_thread_priority": 16, 35 | "lowest_cpu_id": 3, 36 | "highest_cpu_id": 3 37 | } 38 | }, 39 | { 40 | "type": "syscalls", 41 | "value": { 42 | "svcSetHeapSize": "0x01", 43 | "svcSetMemoryPermission": "0x02", 44 | "svcSetMemoryAttribute": "0x03", 45 | "svcMapMemory": "0x04", 46 | "svcUnmapMemory": "0x05", 47 | "svcQueryMemory": "0x06", 48 | "svcExitProcess": "0x07", 49 | "svcCreateThread": "0x08", 50 | "svcStartThread": "0x09", 51 | "svcExitThread": "0x0a", 52 | "svcSleepThread": "0x0b", 53 | "svcGetThreadPriority": "0x0c", 54 | "svcSetThreadPriority": "0x0d", 55 | "svcGetThreadCoreMask": "0x0e", 56 | "svcSetThreadCoreMask": "0x0f", 57 | "svcGetCurrentProcessorNumber": "0x10", 58 | "svcSignalEvent": "0x11", 59 | "svcClearEvent": "0x12", 60 | "svcMapSharedMemory": "0x13", 61 | "svcUnmapSharedMemory": "0x14", 62 | "svcCreateTransferMemory": "0x15", 63 | "svcCloseHandle": "0x16", 64 | "svcResetSignal": "0x17", 65 | "svcWaitSynchronization": "0x18", 66 | "svcCancelSynchronization": "0x19", 67 | "svcArbitrateLock": "0x1a", 68 | "svcArbitrateUnlock": "0x1b", 69 | "svcWaitProcessWideKeyAtomic": "0x1c", 70 | "svcSignalProcessWideKey": "0x1d", 71 | "svcGetSystemTick": "0x1e", 72 | "svcConnectToNamedPort": "0x1f", 73 | "svcSendSyncRequestLight": "0x20", 74 | "svcSendSyncRequest": "0x21", 75 | "svcSendSyncRequestWithUserBuffer": "0x22", 76 | "svcSendAsyncRequestWithUserBuffer": "0x23", 77 | "svcGetProcessId": "0x24", 78 | "svcGetThreadId": "0x25", 79 | "svcBreak": "0x26", 80 | "svcOutputDebugString": "0x27", 81 | "svcReturnFromException": "0x28", 82 | "svcGetInfo": "0x29", 83 | "svcWaitForAddress": "0x34", 84 | "svcSignalToAddress": "0x35", 85 | "svcCreateSession": "0x40", 86 | "svcAcceptSession": "0x41", 87 | "svcReplyAndReceiveLight": "0x42", 88 | "svcReplyAndReceive": "0x43", 89 | "svcReplyAndReceiveWithUserBuffer": "0x44", 90 | "svcCreateEvent": "0x45", 91 | "svcCreateInterruptEvent": "0x53", 92 | "svcReadWriteRegister": "0x4E", 93 | "svcQueryIoMapping": "0x55", 94 | "svcCreateDeviceAddressSpace": "0x56", 95 | "svcAttachDeviceAddressSpace": "0x57", 96 | "svcDetachDeviceAddressSpace": "0x58", 97 | "svcMapDeviceAddressSpaceAligned": "0x5a", 98 | "svcUnmapDeviceAddressSpace": "0x5c", 99 | "svcGetSystemInfo": "0x6f", 100 | "svcCallSecureMonitor": "0x7f" 101 | } 102 | }, 103 | { 104 | "type": "min_kernel_version", 105 | "value": "0x0030" 106 | } 107 | ] 108 | } -------------------------------------------------------------------------------- /source/nfp_user_interface.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Atmosphère-NX 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms and conditions of the GNU General Public License, 6 | * version 2, as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 | * more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "nfp_user_interface.hpp" 23 | 24 | 25 | extern std::atomic_bool g_key_combo_triggered; 26 | extern IEvent* g_activate_event; 27 | extern FILE* g_logging_file; 28 | 29 | struct ModelInfo { 30 | std::array amiibo_identification_block; 31 | u8 padding[0x38]; 32 | }; 33 | static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); 34 | 35 | struct AmiiboFile { 36 | std::array uuid; 37 | u8 padding[0x4a]; 38 | ModelInfo model_info; 39 | }; 40 | static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size"); 41 | 42 | static AmiiboFile GetAmiibo() { 43 | AmiiboFile amiibo{}; 44 | 45 | FILE* file = fopen("amiibo.bin", "rb"); 46 | if (!file) { 47 | fatalSimple(MAKERESULT(Module_Libnx, 42)); 48 | } 49 | fseek(file, 0, SEEK_END); 50 | long fsize = ftell(file); 51 | fseek(file, 0, SEEK_SET); 52 | 53 | fread(&amiibo, fsize, 1, file); 54 | fclose(file); 55 | return amiibo; 56 | } 57 | 58 | NfpUserInterface::NfpUserInterface() { 59 | fprintf(g_logging_file, "Creating NfpUserInterface\n"); 60 | fflush(g_logging_file); 61 | 62 | deactivate_event = CreateWriteOnlySystemEvent(); 63 | availability_change_event = CreateWriteOnlySystemEvent(); 64 | } 65 | 66 | NfpUserInterface::~NfpUserInterface() { 67 | fprintf(g_logging_file, "Destroying NfpUserInterface\n"); 68 | fflush(g_logging_file); 69 | 70 | delete deactivate_event; 71 | delete availability_change_event; 72 | } 73 | 74 | Result NfpUserInterface::Initialize(u64 unk, u64 unk2, PidDescriptor pid_desc, InBuffer buf) { 75 | fprintf(g_logging_file, "Calling initialize\n"); 76 | fflush(g_logging_file); 77 | 78 | state = State::Initialized; 79 | device_state = DeviceState::Initialized; 80 | return 0; 81 | } 82 | 83 | Result NfpUserInterface::Finalize() { 84 | fprintf(g_logging_file, "Calling finalize\n"); 85 | fflush(g_logging_file); 86 | state = State::NonInitialized; 87 | device_state = DeviceState::Finalized; 88 | return 0; 89 | } 90 | 91 | Result NfpUserInterface::GetState(Out out_state) { 92 | fprintf(g_logging_file, "In GetState\n"); 93 | fflush(g_logging_file); 94 | out_state.SetValue(static_cast(state)); 95 | return 0; 96 | } 97 | 98 | Result NfpUserInterface::ListDevices(OutBuffer buffer, Out size) { 99 | fprintf(g_logging_file, "In ListDevices\n"); 100 | fflush(g_logging_file); 101 | memcpy(buffer.buffer, &device_handle, sizeof(device_handle)); 102 | size.SetValue(1); 103 | return 0; 104 | } 105 | 106 | Result NfpUserInterface::GetNpadId(u64 handle, Out out_npad_id) { 107 | fprintf(g_logging_file, "In GetNpadId\n"); 108 | fflush(g_logging_file); 109 | out_npad_id.SetValue(npad_id); 110 | return 0; 111 | } 112 | 113 | Result NfpUserInterface::AttachActivateEvent(u64 handle, Out event) { 114 | fprintf(g_logging_file, "In AttachActivateEvent\n"); 115 | fflush(g_logging_file); 116 | event.SetValue(g_activate_event->GetHandle()); 117 | has_attached_handle = true; 118 | return 0; 119 | } 120 | 121 | Result NfpUserInterface::AttachDeactivateEvent(u64 handle, Out event) { 122 | fprintf(g_logging_file, "In AttachDeactivateEvent\n"); 123 | fflush(g_logging_file); 124 | event.SetValue(deactivate_event->GetHandle()); 125 | return 0; 126 | } 127 | 128 | Result NfpUserInterface::StopDetection() { 129 | fprintf(g_logging_file, "In StopDetection\n"); 130 | fflush(g_logging_file); 131 | switch (device_state) { 132 | case DeviceState::TagFound: 133 | case DeviceState::TagNearby: 134 | deactivate_event->Signal(); 135 | device_state = DeviceState::Initialized; 136 | break; 137 | case DeviceState::SearchingForTag: 138 | case DeviceState::TagRemoved: 139 | device_state = DeviceState::Initialized; 140 | break; 141 | } 142 | return 0; 143 | } 144 | 145 | Result NfpUserInterface::GetDeviceState(Out out_state) { 146 | fprintf(g_logging_file, "In GetDeviceState\n"); 147 | fflush(g_logging_file); 148 | if (g_key_combo_triggered && !has_attached_handle) { 149 | fprintf(g_logging_file, "Triggered GetDeviceState\n"); 150 | fflush(g_logging_file); 151 | 152 | device_state = DeviceState::TagFound; 153 | g_key_combo_triggered = false; 154 | g_activate_event->Clear(); 155 | } 156 | 157 | out_state.SetValue(static_cast(device_state)); 158 | return 0; 159 | } 160 | 161 | Result NfpUserInterface::StartDetection() { 162 | fprintf(g_logging_file, "In StartDetection\n"); 163 | fflush(g_logging_file); 164 | if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { 165 | device_state = DeviceState::SearchingForTag; 166 | } 167 | return 0; 168 | } 169 | 170 | Result NfpUserInterface::GetTagInfo(OutBuffer buffer) { 171 | auto amiibo = GetAmiibo(); 172 | 173 | TagInfo tag_info{}; 174 | tag_info.uuid = amiibo.uuid; 175 | tag_info.uuid_length = static_cast(tag_info.uuid.size()); 176 | 177 | tag_info.protocol = 1; // TODO(ogniK): Figure out actual values 178 | tag_info.tag_type = 2; 179 | 180 | memcpy(buffer.buffer, &tag_info, sizeof(tag_info)); 181 | return 0; 182 | } 183 | 184 | Result NfpUserInterface::Mount() { 185 | device_state = DeviceState::TagNearby; 186 | return 0; 187 | } 188 | 189 | Result NfpUserInterface::GetModelInfo(OutBuffer buffer) { 190 | auto amiibo = GetAmiibo(); 191 | memcpy(buffer.buffer, &amiibo.model_info, sizeof(amiibo.model_info)); 192 | return 0; 193 | } 194 | 195 | Result NfpUserInterface::Unmount() { 196 | device_state = DeviceState::TagFound; 197 | return 0; 198 | } 199 | 200 | Result NfpUserInterface::AttachAvailabilityChangeEvent(Out event) { 201 | event.SetValue(availability_change_event->GetHandle()); 202 | return 0; 203 | } 204 | 205 | Result NfpUserInterface::GetRegisterInfo() { 206 | return 0; 207 | } 208 | 209 | Result NfpUserInterface::GetCommonInfo(OutBuffer buffer) { 210 | CommonInfo common_info{}; 211 | common_info.application_area_size = 0; 212 | memcpy(buffer.buffer, &common_info, sizeof(CommonInfo)); 213 | return 0; 214 | } 215 | 216 | Result NfpUserInterface::OpenApplicationArea() { 217 | return 0; 218 | } 219 | 220 | Result NfpUserInterface::GetApplicationAreaSize(Out size) { 221 | size.SetValue(0); 222 | return 0; 223 | } 224 | 225 | Result NfpUserInterface::GetApplicationArea(Out unk) { 226 | unk.SetValue(0); 227 | return 0; 228 | } -------------------------------------------------------------------------------- /source/nfp_user_interface.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Atmosphère-NX 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms and conditions of the GNU General Public License, 6 | * version 2, as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 | * more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | #pragma once 18 | #include 19 | #include 20 | #include 21 | 22 | enum NfpUserInterfaceCmd : u32 { 23 | NfpUserInterfaceCmd_Initialize = 0, 24 | NfpUserInterfaceCmd_Finalize = 1, 25 | NfpUserInterfaceCmd_ListDevices = 2, 26 | NfpUserInterfaceCmd_StartDetection = 3, 27 | NfpUserInterfaceCmd_StopDetection = 4, 28 | NfpUserInterfaceCmd_Mount = 5, 29 | NfpUserInterfaceCmd_Unmount = 6, 30 | NfpUserInterfaceCmd_OpenApplicationArea = 7, 31 | NfpUserInterfaceCmd_GetApplicationArea = 8, 32 | 33 | 34 | 35 | 36 | NfpUserInterfaceCmd_GetTagInfo = 13, 37 | NfpUserInterfaceCmd_GetRegisterInfo = 14, 38 | NfpUserInterfaceCmd_GetCommonInfo = 15, 39 | NfpUserInterfaceCmd_GetModelInfo = 16, 40 | NfpUserInterfaceCmd_AttachActivateEvent = 17, 41 | NfpUserInterfaceCmd_AttachDeactivateEvent = 18, 42 | NfpUserInterfaceCmd_GetState = 19, 43 | NfpUserInterfaceCmd_GetDeviceState = 20, 44 | NfpUserInterfaceCmd_GetNpadId = 21, 45 | NfpUserInterfaceCmd_GetApplicationAreaSize = 22, 46 | NfpUserInterfaceCmd_AttachAvailabilityChangeEvent = 23, 47 | }; 48 | 49 | struct TagInfo { 50 | std::array uuid; 51 | u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it 52 | // mean something else 53 | u8 pad1[0x15]; 54 | u32 protocol; 55 | u32 tag_type; 56 | u8 pad2[0x2c]; 57 | }; 58 | static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size"); 59 | 60 | enum class State : u32 { 61 | NonInitialized = 0, 62 | Initialized = 1, 63 | }; 64 | 65 | enum class DeviceState : u32 { 66 | Initialized = 0, 67 | SearchingForTag = 1, 68 | TagFound = 2, 69 | TagRemoved = 3, 70 | TagNearby = 4, 71 | Unknown5 = 5, 72 | Finalized = 6 73 | }; 74 | 75 | struct CommonInfo { 76 | u16 last_write_year; // be 77 | u8 last_write_month; 78 | u8 last_write_day; 79 | u16 write_counter; // be 80 | u16 version; // be 81 | u32 application_area_size; // be 82 | u8 padding[0x34]; 83 | }; 84 | static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); 85 | 86 | class NfpUserInterface : public IServiceObject { 87 | public: 88 | NfpUserInterface(); 89 | ~NfpUserInterface(); 90 | 91 | private: 92 | /* Actual command API. */ 93 | virtual Result Initialize(u64 unk, u64 unk2, PidDescriptor pid_desc, InBuffer buf) final; 94 | virtual Result GetState(Out state) final; 95 | virtual Result ListDevices(OutBuffer buffer, Out size) final; 96 | virtual Result GetNpadId(u64 handle, Out npad_id) final; 97 | virtual Result AttachActivateEvent(u64 handle, Out event) final; 98 | virtual Result AttachDeactivateEvent(u64 handle, Out event) final; 99 | virtual Result StopDetection() final; 100 | virtual Result GetDeviceState(Out state) final; 101 | virtual Result StartDetection() final; 102 | virtual Result GetTagInfo(OutBuffer buffer) final; 103 | virtual Result Mount() final; 104 | virtual Result GetModelInfo(OutBuffer buffer) final; 105 | virtual Result Unmount() final; 106 | virtual Result Finalize() final; 107 | virtual Result AttachAvailabilityChangeEvent(Out event) final; 108 | virtual Result GetRegisterInfo() final; 109 | virtual Result GetCommonInfo(OutBuffer buffer) final; 110 | virtual Result OpenApplicationArea() final; 111 | virtual Result GetApplicationAreaSize(Out size) final; 112 | virtual Result GetApplicationArea(Out unk) final; 113 | 114 | bool has_attached_handle{}; 115 | const u64 device_handle{0x555A5559}; // 'YUZU' 116 | const u32 npad_id{0}; // Player 1 controller 117 | State state{State::NonInitialized}; 118 | DeviceState device_state{DeviceState::Initialized}; 119 | IEvent* deactivate_event; 120 | IEvent* availability_change_event; 121 | 122 | public: 123 | DEFINE_SERVICE_DISPATCH_TABLE { 124 | MakeServiceCommandMeta(), 125 | MakeServiceCommandMeta(), 126 | MakeServiceCommandMeta(), 127 | MakeServiceCommandMeta(), 128 | MakeServiceCommandMeta(), 129 | MakeServiceCommandMeta(), 130 | MakeServiceCommandMeta(), 131 | MakeServiceCommandMeta(), 132 | MakeServiceCommandMeta(), 133 | MakeServiceCommandMeta(), 134 | MakeServiceCommandMeta(), 135 | MakeServiceCommandMeta(), 136 | MakeServiceCommandMeta(), 137 | MakeServiceCommandMeta(), 138 | MakeServiceCommandMeta(), 139 | MakeServiceCommandMeta(), 140 | MakeServiceCommandMeta(), 141 | MakeServiceCommandMeta(), 142 | MakeServiceCommandMeta(), 143 | MakeServiceCommandMeta(), 144 | }; 145 | }; 146 | -------------------------------------------------------------------------------- /source/nfpmitm_main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Atmosphère-NX 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms and conditions of the GNU General Public License, 6 | * version 2, as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 | * more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include "nfpuser_mitm_service.hpp" 29 | 30 | extern "C" { 31 | extern u32 __start__; 32 | 33 | u32 __nx_applet_type = AppletType_None; 34 | 35 | #define INNER_HEAP_SIZE 0x20000 36 | size_t nx_inner_heap_size = INNER_HEAP_SIZE; 37 | char nx_inner_heap[INNER_HEAP_SIZE]; 38 | 39 | void __libnx_initheap(void); 40 | void __appInit(void); 41 | void __appExit(void); 42 | } 43 | 44 | 45 | void __libnx_initheap(void) { 46 | void* addr = nx_inner_heap; 47 | size_t size = nx_inner_heap_size; 48 | 49 | /* Newlib */ 50 | extern char* fake_heap_start; 51 | extern char* fake_heap_end; 52 | 53 | fake_heap_start = (char*)addr; 54 | fake_heap_end = (char*)addr + size; 55 | } 56 | 57 | void __appInit(void) { 58 | Result rc; 59 | 60 | rc = smInitialize(); 61 | if (R_FAILED(rc)) { 62 | fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_SM)); 63 | } 64 | 65 | rc = fsInitialize(); 66 | if (R_FAILED(rc)) { 67 | fatalSimple(rc); 68 | } 69 | 70 | rc = fsdevMountSdmc(); 71 | if (R_FAILED(rc)) { 72 | fatalSimple(rc); 73 | } 74 | 75 | //CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION); 76 | } 77 | 78 | void __appExit(void) { 79 | /* Cleanup services. */ 80 | fsdevUnmountAll(); 81 | fsExit(); 82 | smExit(); 83 | } 84 | 85 | struct NfpUserManagerOptions { 86 | static const size_t PointerBufferSize = 0x100; 87 | static const size_t MaxDomains = 4; 88 | static const size_t MaxDomainObjects = 0x100; 89 | }; 90 | 91 | using NfpMitmManager = WaitableManager; 92 | 93 | std::atomic_bool g_key_combo_triggered = false; 94 | IEvent* g_activate_event = nullptr; 95 | FILE* g_logging_file = nullptr; 96 | 97 | void HidLoop(void* arg) { 98 | while (true) { 99 | if (R_FAILED(hidInitialize())) { 100 | fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_HID)); 101 | } 102 | 103 | hidScanInput(); 104 | auto keys = hidKeysDown(CONTROLLER_P1_AUTO); 105 | if (!g_key_combo_triggered && ((keys & (KEY_R | KEY_L)) == (KEY_R | KEY_L))) { 106 | fprintf(g_logging_file, "Key combo triggered\n"); 107 | fflush(g_logging_file); 108 | g_key_combo_triggered = true; 109 | g_activate_event->Signal(); 110 | // RebootToRcm(); 111 | } 112 | 113 | hidExit(); 114 | 115 | svcSleepThread(100000000); 116 | } 117 | svcExitThread(); 118 | } 119 | 120 | int main(int argc, char **argv) { 121 | g_logging_file = fopen("nfp_log.log", "a"); 122 | 123 | g_activate_event = CreateWriteOnlySystemEvent(); 124 | 125 | Thread hid_poller_thread = {0}; 126 | consoleDebugInit(debugDevice_SVC); 127 | 128 | if (R_FAILED(threadCreate(&hid_poller_thread, &HidLoop, NULL, 0x4000, 0x15, -2))) { 129 | fatalSimple(MAKERESULT(Module_Libnx, 40)); 130 | return -1; 131 | } 132 | if (R_FAILED(threadStart(&hid_poller_thread))) { 133 | fatalSimple(MAKERESULT(Module_Libnx, 41)); 134 | return -1; 135 | } 136 | 137 | /* TODO: What's a good timeout value to use here? */ 138 | auto server_manager = new NfpMitmManager(1); 139 | 140 | /* Create fsp-srv mitm. */ 141 | AddMitmServerToManager(server_manager, "nfp:user", 4); 142 | 143 | /* Loop forever, servicing our services. */ 144 | server_manager->Process(); 145 | 146 | fclose(g_logging_file); 147 | 148 | delete server_manager; 149 | 150 | return 0; 151 | } 152 | 153 | -------------------------------------------------------------------------------- /source/nfpuser_mitm_service.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Atmosphère-NX 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms and conditions of the GNU General Public License, 6 | * version 2, as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 | * more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | #include 18 | #include 19 | #include "nfpuser_mitm_service.hpp" 20 | 21 | extern FILE* g_logging_file; 22 | 23 | void NfpUserMitmService::PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx) { 24 | fprintf(g_logging_file, "PostProcessing command %u\n", (u32)ctx->cmd_id); 25 | fflush(g_logging_file); 26 | } 27 | 28 | Result NfpUserMitmService::CreateUserInterface(Out> out_storage) { 29 | fprintf(g_logging_file, "In NfpUserMitmService::CreateUserInterface\n"); 30 | fflush(g_logging_file); 31 | 32 | std::shared_ptr storage = std::make_shared(); 33 | out_storage.SetValue(std::move(storage)); 34 | 35 | if (out_storage.IsDomain()) { 36 | fprintf(g_logging_file, "Is domain with objid %u\n", out_storage.GetObjectId()); 37 | fflush(g_logging_file); 38 | 39 | out_storage.ChangeObjectId(2); 40 | } 41 | return 0; 42 | } -------------------------------------------------------------------------------- /source/nfpuser_mitm_service.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Atmosphère-NX 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms and conditions of the GNU General Public License, 6 | * version 2, as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 | * more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | #pragma once 18 | #include 19 | #include 20 | #include "nfp_user_interface.hpp" 21 | 22 | enum NfpUserCmd : u32 { 23 | NfpUserCmd_CreateUserInterface = 0, 24 | }; 25 | 26 | class NfpUserMitmService : public IMitmServiceObject { 27 | public: 28 | NfpUserMitmService(std::shared_ptr s, u64 pid) : IMitmServiceObject(s, pid) { 29 | /* ... */ 30 | } 31 | 32 | static bool ShouldMitm(u64 pid, u64 tid) { 33 | return true; 34 | } 35 | 36 | static void PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx); 37 | 38 | protected: 39 | /* Overridden commands. */ 40 | Result CreateUserInterface(Out> out); 41 | public: 42 | DEFINE_SERVICE_DISPATCH_TABLE { 43 | MakeServiceCommandMeta(), 44 | }; 45 | }; 46 | --------------------------------------------------------------------------------