├── .cargo └── config.toml ├── .gitignore ├── .gitmodules ├── .vscode ├── c_cpp_properties.json └── settings.json ├── 3ds.ld ├── 3dst.ld ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── const_default ├── .github │ └── workflows │ │ └── rust.yml ├── .gitignore ├── .rustfmt.toml ├── Cargo.toml ├── LICENSE ├── README.md ├── derive │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── src │ └── lib.rs └── tests │ ├── derive.rs │ └── trait-impls.rs ├── include ├── constants.h ├── csvc.h ├── font.h ├── func.h ├── global.h ├── init.h ├── main.h ├── ns.h ├── ntr_config.h ├── proc.h ├── rp.h ├── rt.h ├── svc7b.h ├── ui.h └── xprintf.h ├── make.sh ├── prepare.sh └── source ├── boot └── main_boot.c ├── bootloader.s ├── csvc.s ├── entry.c ├── game └── main_game.c ├── init.c ├── menu ├── main_menu.c ├── rp_menu.c └── ui_menu.c ├── misc.s ├── ns.c ├── nwm └── main_nwm.c ├── nwm_misc ├── FecalCommon.cpp ├── FecalCommon.h ├── FecalEncoder.cpp ├── FecalEncoder.h ├── fecal.cpp ├── fecal.h ├── gf256.cpp ├── gf256.h ├── ikcp.c ├── ikcp.h ├── mempool.c ├── mempool.h ├── rp_res.c ├── rp_res.h ├── rp_syn.c └── rp_syn.h ├── nwm_rs ├── Cargo.toml ├── build.rs ├── nwm_rs.h └── src │ ├── dbg.rs │ ├── entries.rs │ ├── entries │ ├── handle_port.rs │ ├── handle_port │ │ ├── safe_impl.rs │ │ └── vars.rs │ ├── start_up.rs │ ├── start_up │ │ ├── safe_impl.rs │ │ └── vars.rs │ ├── thread_aux.rs │ ├── thread_main.rs │ ├── thread_nwm.rs │ ├── thread_nwm │ │ ├── safe_impl.rs │ │ └── vars.rs │ ├── thread_screen.rs │ ├── thread_screen │ │ ├── safe_impl.rs │ │ └── vars.rs │ ├── work_thread.rs │ └── work_thread │ │ ├── safe_impl.rs │ │ └── vars.rs │ ├── fix.rs │ ├── jpeg.rs │ ├── jpeg │ └── vars.rs │ ├── lib.rs │ ├── utils.rs │ ├── vars.rs │ └── vars │ ├── gpu.rs │ └── ranged.rs ├── overlay.c ├── pm └── main_pm.c ├── proc.c ├── rt.c ├── svc7b.c ├── ui.c └── xprintf.c /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "armv6k-nintendo-3ds" 3 | target-dir = "target" 4 | rustflags = ["-C", "panic=abort"] 5 | 6 | [unstable] 7 | build-std = ["core"] 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Rust artifacts 2 | target 3 | rustc-ice* 4 | 5 | *.bin 6 | lib*.a 7 | 8 | # Prerequisites 9 | *.d 10 | 11 | # Object files 12 | *.o 13 | *.ko 14 | *.obj 15 | *.elf 16 | 17 | # Linker output 18 | *.ilk 19 | *.map 20 | *.exp 21 | 22 | # Precompiled Headers 23 | *.gch 24 | *.pch 25 | 26 | # Libraries 27 | *.lib 28 | *.la 29 | *.lo 30 | 31 | # Shared objects (inc. Windows DLLs) 32 | *.dll 33 | *.so 34 | *.so.* 35 | *.dylib 36 | 37 | # Executables 38 | *.exe 39 | *.out 40 | *.app 41 | *.i*86 42 | *.x86_64 43 | *.hex 44 | 45 | # Debug files 46 | *.dSYM/ 47 | *.su 48 | *.idb 49 | *.pdb 50 | 51 | # Kernel Module Compile Results 52 | *.mod* 53 | *.cmd 54 | .tmp_versions/ 55 | modules.order 56 | Module.symvers 57 | Mkfile.old 58 | dkms.conf 59 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libctru"] 2 | path = libctru 3 | url = https://github.com/xzn/libctru.git 4 | branch = ntr_bins 5 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "3ds", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "defines": [ 9 | "__3DS__" 10 | ], 11 | "cStandard": "c17", 12 | "cppStandard": "gnu++17", 13 | "intelliSenseMode": "windows-gcc-arm", 14 | "compilerPath": "${DEVKITARM}/bin/arm-none-eabi-gcc.exe" 15 | } 16 | ], 17 | "version": 4 18 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.cargo.target": "armv6k-nintendo-3ds", 3 | "rust-analyzer.check.allTargets": false, 4 | "clangd.fallbackFlags": [ 5 | "--target=arm-none-eabi", 6 | "--sysroot", 7 | "/opt/devkitpro/devkitARM/arm-none-eabi", 8 | "-I${workspaceFolder}/include", 9 | "-I${workspaceFolder}/libctru/libctru/include", 10 | "-DARM11", 11 | "-D__3DS__", 12 | "-Wno-reserved-user-defined-literal", 13 | "-Wno-c2x-extensions", 14 | ], 15 | } 16 | -------------------------------------------------------------------------------- /3ds.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") 2 | OUTPUT_ARCH(arm) 3 | ENTRY(_Reset) 4 | PHDRS 5 | { 6 | code PT_LOAD FLAGS(5) /* Read | Execute */; 7 | rodata PT_LOAD FLAGS(4) /* Read */; 8 | data PT_LOAD FLAGS(6) /* Read | Write */; 9 | } 10 | SECTIONS 11 | { 12 | . = 0x00100100; 13 | . = ALIGN(4); 14 | .text : { 15 | obj/bootloader.o (.text*) 16 | *(.text*) 17 | } : code 18 | 19 | . = ALIGN(4); 20 | .rodata : { 21 | *(.rodata) 22 | *(.roda) 23 | *(.rodata.*) 24 | *all.rodata*(*) 25 | . = ALIGN(4); 26 | __tdata_align = .; 27 | LONG (ALIGNOF(.tdata)); 28 | . = ALIGN(4); 29 | } : rodata 30 | 31 | . = ALIGN(4); 32 | .rel.dyn : { 33 | __rel_dyn_start = .; 34 | *(.rel) 35 | *(.rel.*) 36 | __rel_dyn_end = .; 37 | } : rodata 38 | 39 | . = ALIGN(4); .dynamic : { *(.dynamic) } : data 40 | . = ALIGN(4); .got : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) } : data 41 | 42 | . = ALIGN(4); 43 | .data : { 44 | *(.data) 45 | *(.data.*) 46 | } : data 47 | 48 | . = ALIGN(4); 49 | .tdata : { 50 | __tdata_lma = .; 51 | *(.tdata) 52 | *(.tdata.*) 53 | . = ALIGN(4); 54 | __tdata_lma_end = .; 55 | } : data 56 | 57 | . = ALIGN(4); 58 | .tbss : { 59 | *(.tbss) 60 | *(.tbss.*) 61 | *(.tcommon) 62 | . = ALIGN(4); 63 | } : data 64 | 65 | . = ALIGN(4); 66 | .bss : { 67 | *(.__bss_start) 68 | *(.dynbss) 69 | *(.bss) 70 | *(.bss.*) 71 | *(COMMON) 72 | 73 | . = 8 + ABSOLUTE(ALIGN(ABSOLUTE(. - 8), ALIGNOF(.tdata))); 74 | __tls_start = .; 75 | . += SIZEOF(.tdata); 76 | 77 | . = ALIGN(ALIGNOF(.tbss)); 78 | . += SIZEOF(.tbss); 79 | __tls_end = .; 80 | 81 | *(.__bss_end) 82 | } : data 83 | 84 | . = ALIGN(4); 85 | __end__ = .; 86 | } 87 | -------------------------------------------------------------------------------- /3dst.ld: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------------- 2 | This Source Code Form is subject to the terms of the Mozilla Public License, 3 | v. 2.0. If a copy of the MPL was not distributed with this file, You can 4 | obtain one at https://mozilla.org/MPL/2.0/. 5 | --------------------------------------------------------------------------------*/ 6 | 7 | /* Adapted from the original NTR 3ds.ld and devkitPro's 3dsx.ld */ 8 | 9 | OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") 10 | OUTPUT_ARCH(arm) 11 | ENTRY(_Reset) 12 | PHDRS 13 | { 14 | code PT_LOAD FLAGS(5) /* Read | Execute */; 15 | rodata PT_LOAD FLAGS(4) /* Read */; 16 | data PT_LOAD FLAGS(6) /* Read | Write */; 17 | } 18 | SECTIONS 19 | { 20 | . = 0x00100100; 21 | . = ALIGN(4); 22 | .text : { 23 | obj/bootloader.o (.text*) 24 | *(.text*) 25 | } : code 26 | 27 | . = ALIGN(4); 28 | .rodata : { 29 | *(.rodata) 30 | *(.roda) 31 | *(.rodata.*) 32 | *all.rodata*(*) 33 | . = ALIGN(4); 34 | __tdata_align = .; 35 | LONG (ALIGNOF(.tdata)); 36 | . = ALIGN(4); 37 | } : rodata 38 | 39 | . = ALIGN(4); 40 | .rel.dyn : { 41 | __rel_dyn_start = .; 42 | *(.rel) 43 | *(.rel.*) 44 | __rel_dyn_end = .; 45 | } : rodata 46 | 47 | /* 48 | .ARM.exidx : 49 | { 50 | PROVIDE_HIDDEN (__exidx_start = .); 51 | *(.ARM.exidx*) 52 | PROVIDE_HIDDEN (__exidx_end = .); 53 | } 54 | */ 55 | 56 | . = ALIGN(4); .dynamic : { *(.dynamic) } : data 57 | . = ALIGN(4); .got : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) } : data 58 | 59 | . = ALIGN(4); 60 | .data : { 61 | *(.data) 62 | *(.data.*) 63 | } : data 64 | 65 | . = ALIGN(4); 66 | .tdata : { 67 | __tdata_lma = .; 68 | *(.tdata) 69 | *(.tdata.*) 70 | . = ALIGN(4); 71 | __tdata_lma_end = .; 72 | } : data 73 | 74 | . = ALIGN(4); 75 | .tbss : { 76 | *(.tbss) 77 | *(.tbss.*) 78 | *(.tcommon) 79 | . = ALIGN(4); 80 | } : data 81 | 82 | . = ALIGN(4); 83 | .bss : { 84 | *(.__bss_start) 85 | *(.dynbss) 86 | *(.bss) 87 | *(.bss.*) 88 | *(COMMON) 89 | 90 | . = 8 + ABSOLUTE(ALIGN(ABSOLUTE(. - 8), ALIGNOF(.tdata))); 91 | __tls_start = .; 92 | . += SIZEOF(.tdata); 93 | 94 | . = ALIGN(ALIGNOF(.tbss)); 95 | . += SIZEOF(.tbss); 96 | __tls_end = .; 97 | 98 | *(.__bss_end) 99 | } : data 100 | 101 | . = ALIGN(4); 102 | __end__ = .; 103 | } 104 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["source/nwm_rs"] 3 | resolver = "2" 4 | 5 | [profile.release] 6 | lto = true 7 | opt-level = 3 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Need 2025-02-02 nightly rust for now 2 | 3 | DEV_BIN_DIR := $(DEVKITARM)/bin 4 | UNAME := $(shell uname) 5 | 6 | # USE_CLANG = 1 7 | # USE_LTO = 1 8 | 9 | # When compiling with clang libclang_rt.builtins-arm.a will need to be obtained elsewhere. 10 | # See https://llvm.org/docs/HowToCrossCompileBuiltinsOnArm.html 11 | 12 | CC_NAME = @echo $(notdir $@); 13 | AS = $(CC_NAME) $(DEV_BIN_DIR)/arm-none-eabi-as 14 | CLANG_FLAGS = -target arm-none-eabi 15 | CLANG_FLAGS += --sysroot $(DEVKITARM)/arm-none-eabi 16 | CLANG_FLAGS += -Wno-c2x-extensions -Wno-reserved-user-defined-literal 17 | 18 | ifeq ($(USE_CLANG),1) 19 | CC = $(CC_NAME) clang $(CLANG_FLAGS) 20 | CXX = $(CC_NAME) clang++ $(CLANG_FLAGS) 21 | RSFLAGS = RUSTFLAGS="-C panic=abort -Clinker-plugin-lto" 22 | else 23 | CC = $(CC_NAME) $(DEV_BIN_DIR)/arm-none-eabi-gcc 24 | CXX = $(CC_NAME) $(DEV_BIN_DIR)/arm-none-eabi-g++ 25 | RSFLAGS = RUSTFLAGS="-C panic=abort" 26 | endif 27 | 28 | ifeq ($(USE_LTO),1) 29 | LTO = -flto 30 | else 31 | LTO = 32 | endif 33 | 34 | OBJCOPY = $(DEV_BIN_DIR)/arm-none-eabi-objcopy 35 | LD = $(DEV_BIN_DIR)/arm-none-eabi-ld 36 | CP = cp 37 | 38 | CTRU_DIR := libctru/libctru 39 | 40 | CFLAGS := -O3 -ffast-math -g -march=armv6k -mtune=mpcore -mfloat-abi=hard -mfpu=vfp -mtp=soft -fno-strict-aliasing -fshort-enums 41 | CFLAGS += -ffunction-sections -fdata-sections 42 | CPPFLAGS := -Iinclude -Ilibctru/libctru/include -D__3DS__ 43 | LDFLAGS = -Wl,--gc-sections -Wl,-Map=$(basename $(notdir $@)).map,-z,notext,-z,noexecstack -L. -L$(LIB_RS_DIR) -L$(DEVKITARM)/arm-none-eabi/lib/armv6k/fpu 44 | LDLIBS := -lctru_ntr -lsysbase 45 | LDLIBS += -Wl,-pie 46 | SRC_C := $(wildcard source/*.c) 47 | SRC_S := $(wildcard source/*.s) 48 | 49 | SRC_BOOT_C := $(wildcard source/boot/*.c) 50 | OBJ_BOOT := $(addprefix obj/,$(notdir $(SRC_BOOT_C:.c=.o))) 51 | 52 | SRC_MENU_C := $(wildcard source/menu/*.c) 53 | OBJ_MENU := $(addprefix obj/,$(notdir $(SRC_MENU_C:.c=.o))) 54 | 55 | SRC_PM_C := $(wildcard source/pm/*.c) 56 | OBJ_PM := $(addprefix obj/,$(notdir $(SRC_PM_C:.c=.o))) 57 | 58 | SRC_GAME_C := $(wildcard source/game/*.c) 59 | OBJ_GAME := $(addprefix obj/,$(notdir $(SRC_GAME_C:.c=.o))) 60 | 61 | SRC_NWM_C := $(wildcard source/nwm/*.c) 62 | SRC_NWM_C += $(wildcard source/nwm_misc/*.c) 63 | SRC_NWM_X := $(wildcard source/nwm_misc/*.cpp) 64 | OBJ_NWM := $(addprefix obj/,$(notdir $(SRC_NWM_C:.c=.o))) 65 | OBJ_NWM += $(addprefix obj/,$(notdir $(SRC_NWM_X:.cpp=.o))) 66 | 67 | OBJ := $(addprefix obj/,$(notdir $(SRC_C:.c=.o) $(SRC_S:.s=.o))) 68 | DEP := $(OBJ:.o=.d) $(OBJ_BOOT:.o=.d) $(OBJ_MENU:.o=.d) $(OBJ_PM:.o=.d) $(OBJ_GAME:.o=.d) $(OBJ_NWM:.o=.d) 69 | 70 | NTR_BIN_BOOT := ntr.hr.boot.bin 71 | NTR_BIN_MENU := ntr.hr.menu.bin 72 | NTR_BIN_PM := ntr.hr.pm.bin 73 | NTR_BIN_NWM := ntr.hr.nwm.bin 74 | NTR_BIN_GAME := ntr.hr.game.bin 75 | 76 | LIB_RS_DIR := target/armv6k-nintendo-3ds/release 77 | LIB_NWM_RS := $(LIB_RS_DIR)/libnwm_rs.a 78 | 79 | PAYLOAD_BIN := $(NTR_BIN_BOOT) $(NTR_BIN_MENU) $(NTR_BIN_PM) $(NTR_BIN_NWM) $(NTR_BIN_GAME) 80 | PAYLOAD_TARGET_DIR := ../BootNTR-Bins/romfs 81 | PAYLOAD_TARGET_BIN := $(addprefix $(PAYLOAD_TARGET_DIR)/,$(PAYLOAD_BIN)) 82 | 83 | PAYLOAD_LOCAL_BIN := $(addprefix release/,$(PAYLOAD_BIN)) 84 | PAYLOAD_LOCAL_ELF := $(addprefix bin/,$(PAYLOAD_BIN:.bin=.elf)) 85 | 86 | all: $(PAYLOAD_LOCAL_BIN) $(PAYLOAD_LOCAL_ELF) 87 | 88 | install: $(PAYLOAD_TARGET_BIN) 89 | 90 | .NOTPARALLEL: rs 91 | 92 | rs: $(LIB_NWM_RS) 93 | 94 | CP_CMD = @echo \* $(notdir $@) \*; $(CP) $< $@ 95 | 96 | $(PAYLOAD_TARGET_DIR)/%.bin: release/%.bin 97 | $(CP_CMD) 98 | 99 | release/%.bin: bin/%.elf | release 100 | $(CC_NAME) $(OBJCOPY) -O binary $< $@ -S 101 | 102 | release: 103 | mkdir $@ 104 | 105 | bin/$(NTR_BIN_BOOT:.bin=.elf): $(OBJ) $(OBJ_BOOT) libctru_ntr.a 3ds.ld | bin 106 | $(CC) -flto=auto $(CFLAGS) -o $@ -T 3ds.ld $(LDFLAGS) $(OBJ) $(OBJ_BOOT) $(LDLIBS) 107 | 108 | bin/$(NTR_BIN_MENU:.bin=.elf): $(OBJ) $(OBJ_MENU) libctru_ntr.a 3ds.ld | bin 109 | $(CC) -flto=auto $(CFLAGS) -o $@ -T 3ds.ld $(LDFLAGS) $(OBJ) $(OBJ_MENU) $(LDLIBS) 110 | 111 | bin/$(NTR_BIN_PM:.bin=.elf): $(OBJ) $(OBJ_PM) libctru_ntr.a 3ds.ld | bin 112 | $(CC) -flto=auto $(CFLAGS) -o $@ -T 3ds.ld $(LDFLAGS) $(OBJ) $(OBJ_PM) $(LDLIBS) 113 | 114 | bin/$(NTR_BIN_GAME:.bin=.elf): $(OBJ) $(OBJ_GAME) libctru_ntr.a 3ds.ld | bin 115 | $(CC) -flto=auto $(CFLAGS) -o $@ -T 3ds.ld $(LDFLAGS) $(OBJ) $(OBJ_GAME) $(LDLIBS) 116 | 117 | bin/$(NTR_BIN_NWM:.bin=.elf): $(OBJ) $(OBJ_NWM) libctru_ntr.a 3dst.ld $(LIB_NWM_RS) | bin 118 | $(CC) -flto=auto $(CFLAGS) -o $@ -T 3dst.ld $(LDFLAGS) $(OBJ) $(OBJ_NWM) $(LDLIBS) -lnwm_rs -lm 119 | 120 | bin: 121 | mkdir $@ 122 | 123 | $(LIB_NWM_RS): $(shell find source/nwm_rs -type f) $(shell find . -name '*.h' -type f) 124 | $(RSFLAGS) cargo -Z unstable-options -C source/nwm_rs build --release 125 | 126 | libctru_ntr.a: $(CTRU_DIR)/lib/libctru.a 127 | $(CP_CMD) 128 | 129 | $(CTRU_DIR)/lib/libctru.a: 130 | $(MAKE) -C $(CTRU_DIR) lib/libctru.a 131 | 132 | CC_WARNS = -Wall -Wextra 133 | 134 | CC_CMD = $(CC) $(LTO) $(CFLAGS) $(CPPFLAGS) -MMD -c -o $@ $< $(CC_WARNS) 135 | NWM_CC_CMD = $(CC) $(LTO) $(CFLAGS) $(CPPFLAGS) -MMD -c -o $@ $< $(CC_WARNS) 136 | NWM_CXX_CMD = $(CXX) $(LTO) $(CFLAGS) $(CPPFLAGS) -fno-exceptions -MMD -c -o $@ $< $(CC_WARNS) -Wno-implicit-fallthrough 137 | 138 | obj/%.o: source/%.s | obj 139 | $(AS) -march=armv6k -mfloat-abi=hard -o $@ $< 140 | 141 | obj/%.o: source/%.c | obj 142 | $(CC_CMD) 143 | 144 | obj/%.o: source/boot/%.c | obj 145 | $(CC_CMD) 146 | 147 | obj/%.o: source/menu/%.c | obj 148 | $(CC_CMD) -DNTR_BIN_PM=\"$(NTR_BIN_PM)\" -DNTR_BIN_NWM=\"$(NTR_BIN_NWM)\" 149 | 150 | obj/%.o: source/pm/%.c | obj 151 | $(CC_CMD) -DNTR_BIN_GAME=\"$(NTR_BIN_GAME)\" 152 | 153 | obj/%.o: source/game/%.c | obj 154 | $(CC_CMD) 155 | 156 | obj/%.o: source/nwm/%.c | obj 157 | $(NWM_CC_CMD) 158 | 159 | obj/%.o: source/nwm_misc/%.c | obj 160 | $(NWM_CC_CMD) 161 | 162 | obj/%.o: source/nwm_misc/%.cpp | obj 163 | $(NWM_CXX_CMD) 164 | 165 | obj/nwm_lto.o: $(OBJ_NWM) | obj 166 | $(CC) -flto $(CFLAGS) -L. -r -o $@ $^ 167 | 168 | obj: 169 | mkdir $@ 170 | 171 | -include $(DEP) 172 | 173 | .PHONY: clean all install rs 174 | 175 | clean: 176 | -rm *.map bin/* release/* obj/* libctru_ntr.a 177 | -rm target/ -rf 178 | $(MAKE) -C $(CTRU_DIR) clean 179 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Improved frame rate for NTR wireless streaming with New 3DS (and some other misc streaming-related changes). 2 | 3 | For use with [Snickerstream](https://github.com/RattletraPM/Snickerstream), [Chokistream](https://github.com/Eiim/Chokistream), or any [NTR](https://www.gamebrew.org/wiki/NTRViewer_3DS) compatible viewers. 4 | 5 | ## Summary 6 | 7 | NTR is a homebrew originally made by cell9 that allows game patching, debugging, and wireless streaming on 3DS. 8 | 9 | This fork attempts to improve the wireless streaming aspect of the homebrew. Currently it is able achieve 60 ~ 90 fps for average quality settings (at 18Mbps). 10 | 11 | ## Changes from [NTR 3.6](https://github.com/44670/NTR) 12 | 13 | - Use up to three cores for encoding 14 | - - Improved frame rate by around 80% to 120% compared to 3.6 15 | - Can now switch between games and keep the stream going 16 | - No more green tint when streaming games with RGB565 output (e.g. SMT4) 17 | - Can now update quality setting, QoS, and screen priority settings while streaming 18 | - New menu item in NTR menu (accessed by X + Y), can be used to change viewer's port and other settings 19 | - - Has NFC patching functionality 20 | - - Can start Remote Play from the menu and change viewer's IP 21 | - Skip duplicate frames (when actual frame rate is lower than how fast NTR can encode) 22 | - - Should lead to better frame pacing 23 | - Various optimizations and updated dependencies 24 | - Fixed race conditions in startup unhooking code. Should no longer crash/fail to initialize randomly when starting NTR, or when starting remote play. 25 | 26 | Removed all other features aside from streaming and PLG loading: 27 | 28 | - Screenshots, debugger, and night color are available with Luma3DS/rosalina 29 | - Real-time save/load doesn't save/load handles (and has no way of doing so) and generally doesn't work very well. I can add this back in if needed. 30 | - No way to disable high CPU clock/L2 cache after starting remote play. I may add an option for this. 31 | 32 | ## Known issues 33 | 34 | - Skip duplicate frames doesn't work as expected sometimes. 35 | - Some games are not compatible with streaming. 36 | - When cheat plugins have been loaded, launching another game (or the same game again) would hang for certain plugins. 37 | - [UWPStreamer](https://github.com/toolboc/UWPStreamer) flickers and crashes sometimes. 38 | - [NTRView for Wii U](https://github.com/yawut/ntrview-wiiu) flickers especially when core count is set to more than one. 39 | - 3DS web browser will crash if used with remote play. 40 | 41 | ## Credits 42 | 43 | - cell9 44 | - Nanquitas 45 | - PabloMK7 46 | 47 | Especially thanks to cell9 for releasing the source of NTR 3.6 making this mod possible 48 | 49 | 50 | ## Recommended setup 51 | 52 | ### Connecting 3DS to your PC 53 | 54 | A WiFi dongle dedicated to host a hotspot for the 3DS is recommended. You can go to device manager and disable 5GHz mode for the WiFi dongle you are using for hotspot to maximize connection stability and connection speed for 3DS. If you have something like a Raspberry Pi you can connect the RPI to PC with Ethernet and run the hotspot off the RPI instead (with bridged connection). This should guarantee a connection speed up to 18Mbps without dropping too many packets. 55 | 56 | ### Viewer settings 57 | 58 | Quality is recommended to be between 75 and 90. At 75 you'd get between 55 to 75 fps. 40 to 60 fps for quality 90. (Tested on N2DS. N3DS seems to have 10% to 15% lower streaming performance) (Assuming good WiFi connection, which you can have if you use a dedicated 2.4GHz only hotspot) 59 | 60 | QoS can be up to 18 for around 18Mbps. Higher values will cause more packet drops. 61 | 62 | Priority settings is up to your personal preference. 63 | -------------------------------------------------------------------------------- /const_default/.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust - Continuous Integration 2 | 3 | on: 4 | push: 5 | branches: [ '*' ] 6 | tags: [ '*' ] 7 | pull_request: 8 | branches: [ '*' ] 9 | 10 | jobs: 11 | check: 12 | name: Check 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - uses: actions-rs/toolchain@v1 17 | with: 18 | profile: minimal 19 | toolchain: stable 20 | override: true 21 | - uses: actions-rs/cargo@v1 22 | with: 23 | command: check 24 | 25 | test: 26 | name: Test 27 | strategy: 28 | matrix: 29 | os: [ubuntu-latest, windows-latest, macos-latest] 30 | runs-on: ${{ matrix.os }} 31 | steps: 32 | - uses: actions/checkout@v2 33 | - uses: actions-rs/toolchain@v1 34 | with: 35 | profile: minimal 36 | toolchain: stable 37 | override: true 38 | - uses: actions-rs/cargo@v1 39 | with: 40 | command: test 41 | - uses: actions-rs/cargo@v1 42 | with: 43 | command: test 44 | args: --features alloc 45 | - uses: actions-rs/cargo@v1 46 | with: 47 | command: test 48 | args: --features std 49 | 50 | fmt: 51 | name: Formatting 52 | runs-on: ubuntu-latest 53 | steps: 54 | - uses: actions/checkout@v2 55 | - uses: actions-rs/toolchain@v1 56 | with: 57 | profile: minimal 58 | toolchain: nightly 59 | override: true 60 | components: rustfmt 61 | - uses: actions-rs/cargo@v1 62 | with: 63 | command: fmt 64 | args: --all -- --check 65 | 66 | doc: 67 | name: Documentation 68 | runs-on: ubuntu-latest 69 | steps: 70 | - uses: actions/checkout@v2 71 | - uses: actions-rs/toolchain@v1 72 | with: 73 | profile: minimal 74 | toolchain: stable 75 | override: true 76 | components: rust-docs, rust-src 77 | - uses: actions-rs/cargo@v1 78 | env: 79 | RUSTDOCFLAGS: '-D warnings' 80 | with: 81 | command: doc 82 | args: --workspace --no-deps --document-private-items 83 | 84 | clippy: 85 | name: Clippy 86 | runs-on: ubuntu-latest 87 | steps: 88 | - uses: actions/checkout@v2 89 | - uses: actions-rs/toolchain@v1 90 | with: 91 | profile: minimal 92 | toolchain: stable 93 | override: true 94 | components: clippy 95 | - uses: actions-rs/cargo@v1 96 | with: 97 | command: clippy 98 | args: --workspace -- -D warnings 99 | 100 | audit: 101 | name: Audit 102 | runs-on: ubuntu-latest 103 | steps: 104 | - uses: actions/checkout@v2 105 | - uses: actions-rs/toolchain@v1 106 | with: 107 | profile: minimal 108 | toolchain: stable 109 | override: true 110 | - uses: actions-rs/cargo@v1 111 | with: 112 | command: audit 113 | args: --deny warnings 114 | -------------------------------------------------------------------------------- /const_default/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /const_default/.rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | normalize_comments = true 3 | imports_indent = "Block" 4 | imports_layout = "HorizontalVertical" 5 | imports_granularity="Crate" 6 | combine_control_expr = false 7 | force_multiline_blocks = true 8 | trailing_semicolon = false 9 | edition = "2021" 10 | use_try_shorthand = true 11 | use_field_init_shorthand = true 12 | -------------------------------------------------------------------------------- /const_default/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "const-default" 3 | version = "1.0.0" 4 | edition = "2018" 5 | authors = ["AerialX"] 6 | 7 | categories = ["no-std"] 8 | 9 | description = "A const Default trait" 10 | documentation = "https://docs.rs/const-default" 11 | repository = "https://github.com/AerialX/const-default.rs" 12 | readme = "README.md" 13 | license = "MIT" 14 | 15 | [dependencies] 16 | const-default-derive = { path = "derive", version = "0.2.0", optional = true } 17 | 18 | [features] 19 | default = ["enable-atomics"] 20 | std = ["alloc"] 21 | alloc = [] 22 | derive = ["const-default-derive"] 23 | unstable = [] 24 | unstable-docs = [] 25 | enable-atomics = [] 26 | 27 | [package.metadata.docs.rs] 28 | all-features = true 29 | -------------------------------------------------------------------------------- /const_default/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 by Aaron Lindsay 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /const_default/README.md: -------------------------------------------------------------------------------- 1 | # ConstDefault Trait 2 | 3 | [![Crates.io](https://img.shields.io/crates/v/const-default)](https://crates.io/crates/const-default) [![Crates.io](https://img.shields.io/crates/l/const-default)](LICENSE) [![docs.rs](https://img.shields.io/docsrs/const-default)](https://docs.rs/const-default) [![actions](https://github.com/AerialX/const-default.rs/actions/workflows/rust.yml/badge.svg)](https://github.com/AerialX/const-default.rs/actions/workflows/rust.yml) 4 | 5 | A `Default`-like trait and derive macros for `const` evaluation contexts. 6 | 7 | This crate defines the `ConstDefault` trait and implements it for 8 | Rust primitives, prelude types, tuples and arrays. Furthermore it 9 | provides a derive macro so that users can implement `ConstDefault` 10 | easily for their custom types. 11 | 12 | - 100% safe Rust 13 | - `no_std` compatible 14 | - Full macro hygiene 15 | - No dependencies 16 | 17 | ## Usage 18 | 19 | Add 20 | ```toml 21 | [dependencies] 22 | const-default = { version = "1.0", features = ["derive"] } 23 | ``` 24 | to your `Cargo.toml` to start using it. 25 | 26 | ## Examples 27 | 28 | ### Rust Primitives 29 | 30 | ```rust 31 | use const_default::ConstDefault; 32 | 33 | fn main() { 34 | assert_eq!(::DEFAULT, 0); 35 | assert_eq!( as ConstDefault>::DEFAULT, None); 36 | assert_eq!(::DEFAULT, String::new()); 37 | assert_eq!( as ConstDefault>::DEFAULT, Vec::new()); 38 | } 39 | ``` 40 | 41 | ### Derive 42 | 43 | ```rust 44 | use const_default::ConstDefault; 45 | 46 | #[derive(ConstDefault, Debug, Default, PartialEq)] 47 | pub struct Color { 48 | r: u8, 49 | g: u8, 50 | b: u8, 51 | } 52 | 53 | fn main() { 54 | assert_eq!( 55 | ::DEFAULT, 56 | Color::default(), 57 | ); 58 | } 59 | ``` 60 | -------------------------------------------------------------------------------- /const_default/derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "const-default-derive" 3 | version = "0.2.0" 4 | edition = "2018" 5 | authors = ["AerialX"] 6 | 7 | description = "#[derive(ConstDefault)]" 8 | documentation = "https://docs.rs/const-default-derive" 9 | repository = "https://github.com/AerialX/const-default.rs" 10 | readme = "../README.md" 11 | license = "MIT" 12 | 13 | [lib] 14 | proc-macro = true 15 | 16 | [dependencies] 17 | proc-macro-crate = "1" 18 | proc-macro2 = "1" 19 | quote = "1" 20 | syn = { version = "1", default-features = false, features = ["derive", 21 | "parsing", "proc-macro", "printing"] } 22 | 23 | [dev-dependencies] 24 | const-default = { path = "..", features = ["derive"] } 25 | -------------------------------------------------------------------------------- /const_default/derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc(html_root_url = "http://docs.rs/const-default-derive/0.2.0")] 2 | 3 | extern crate proc_macro; 4 | 5 | use proc_macro::TokenStream; 6 | use proc_macro2::{Ident, Literal, Span, TokenStream as TokenStream2}; 7 | use proc_macro_crate::{crate_name, FoundCrate}; 8 | use quote::{quote, quote_spanned}; 9 | use syn::{spanned::Spanned, Error}; 10 | 11 | /// Derives an implementation for the [`ConstDefault`] trait. 12 | /// 13 | /// # Note 14 | /// 15 | /// Currently only works with `struct` inputs. 16 | /// 17 | /// # Example 18 | /// 19 | /// ## Struct 20 | /// 21 | /// ``` 22 | /// # use const_default::ConstDefault; 23 | /// #[derive(ConstDefault)] 24 | /// # #[derive(Debug, PartialEq)] 25 | /// pub struct Color { 26 | /// r: u8, 27 | /// g: u8, 28 | /// b: u8, 29 | /// } 30 | /// 31 | /// assert_eq!( 32 | /// ::DEFAULT, 33 | /// Color { r: 0, g: 0, b: 0 }, 34 | /// ) 35 | /// ``` 36 | /// 37 | /// ## Tuple Struct 38 | /// 39 | /// ``` 40 | /// # use const_default::ConstDefault; 41 | /// #[derive(ConstDefault)] 42 | /// # #[derive(Debug, PartialEq)] 43 | /// pub struct Vec3(f32, f32, f32); 44 | /// 45 | /// assert_eq!( 46 | /// ::DEFAULT, 47 | /// Vec3(0.0, 0.0, 0.0), 48 | /// ) 49 | /// ``` 50 | /// 51 | /// ## Union 52 | /// 53 | /// ``` 54 | /// const CAT_SIZE: usize = 0x16; 55 | /// # use const_default::ConstDefault; 56 | /// #[derive(ConstDefault)] 57 | /// pub union Animal { 58 | /// Dog: u8, 59 | /// Cat: [u8; CAT_SIZE], 60 | /// } 61 | /// 62 | /// assert_eq!( 63 | /// unsafe { ::DEFAULT.Cat }, 64 | /// [0; CAT_SIZE], 65 | /// ) 66 | /// ``` 67 | #[proc_macro_derive(ConstDefault, attributes(const_default))] 68 | pub fn derive(input: TokenStream) -> TokenStream { 69 | match derive_default(input.into()) { 70 | Ok(output) => output.into(), 71 | Err(error) => error.to_compile_error().into(), 72 | } 73 | } 74 | 75 | /// Implements the derive of `#[derive(ConstDefault)]` for struct types. 76 | fn derive_default(input: TokenStream2) -> Result { 77 | let crate_ident = query_crate_ident()?; 78 | let input = syn::parse2::(input)?; 79 | let ident = input.ident; 80 | let (default_impl, generics) = match input.data { 81 | syn::Data::Struct(data_struct) => { 82 | let default_impl = 83 | generate_default_impl_struct(&crate_ident, &data_struct)?; 84 | let mut generics = input.generics; 85 | generate_default_impl_where_bounds( 86 | &crate_ident, 87 | &data_struct, 88 | &mut generics, 89 | )?; 90 | (default_impl, generics) 91 | } 92 | syn::Data::Union(data_union) => { 93 | let default_impl = 94 | generate_default_impl_union(&crate_ident, &data_union)?; 95 | let mut generics = input.generics; 96 | generate_default_impl_union_where_bounds( 97 | &crate_ident, 98 | &data_union, 99 | &mut generics, 100 | )?; 101 | (default_impl, generics) 102 | } 103 | _ => { 104 | return Err(Error::new( 105 | Span::call_site(), 106 | "ConstDefault derive only works on struct types", 107 | )) 108 | } 109 | }; 110 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 111 | Ok(quote! { 112 | impl #impl_generics #crate_ident::ConstDefault for #ident #ty_generics #where_clause { 113 | const DEFAULT: Self = #default_impl; 114 | } 115 | }) 116 | } 117 | 118 | /// Queries the dependencies for the derive root crate name and returns the identifier. 119 | /// 120 | /// # Note 121 | /// 122 | /// This allows to use crate aliases in `Cargo.toml` files of dependencies. 123 | fn query_crate_ident() -> Result { 124 | let query = crate_name("const-default").map_err(|error| { 125 | Error::new( 126 | Span::call_site(), 127 | format!( 128 | "could not find root crate for ConstDefault derive: {}", 129 | error 130 | ), 131 | ) 132 | })?; 133 | match query { 134 | FoundCrate::Itself => Ok(quote! { crate }), 135 | FoundCrate::Name(name) => { 136 | let ident = Ident::new(&name, Span::call_site()); 137 | Ok(quote! { ::#ident }) 138 | } 139 | } 140 | } 141 | 142 | /// Generates the `ConstDefault` implementation for `struct` input types. 143 | /// 144 | /// # Note 145 | /// 146 | /// The generated code abuses the fact that in Rust struct types can always 147 | /// be represented with braces and either using identifiers for fields or 148 | /// raw number literals in case of tuple-structs. 149 | /// 150 | /// For example `struct Foo(u32)` can be represented as `Foo { 0: 42 }`. 151 | fn generate_default_impl_struct( 152 | crate_ident: &TokenStream2, 153 | data_struct: &syn::DataStruct, 154 | ) -> Result { 155 | let fields_impl = 156 | data_struct.fields.iter().enumerate().map(|(n, field)| { 157 | let field_span = field.span(); 158 | let field_type = &field.ty; 159 | let field_pos = Literal::usize_unsuffixed(n); 160 | let field_ident = field 161 | .ident 162 | .as_ref() 163 | .map(|ident| quote_spanned!(field_span=> #ident)) 164 | .unwrap_or_else(|| quote_spanned!(field_span=> #field_pos)); 165 | quote_spanned!(field_span=> 166 | #field_ident: <#field_type as #crate_ident::ConstDefault>::DEFAULT 167 | ) 168 | }); 169 | Ok(quote! { 170 | Self { 171 | #( #fields_impl ),* 172 | } 173 | }) 174 | } 175 | 176 | /// Generates `ConstDefault` where bounds for all fields of the input. 177 | fn generate_default_impl_where_bounds( 178 | crate_ident: &TokenStream2, 179 | data_struct: &syn::DataStruct, 180 | generics: &mut syn::Generics, 181 | ) -> Result<(), syn::Error> { 182 | let where_clause = generics.make_where_clause(); 183 | for field in &data_struct.fields { 184 | let field_type = &field.ty; 185 | where_clause.predicates.push(syn::parse_quote!( 186 | #field_type: #crate_ident::ConstDefault 187 | )) 188 | } 189 | Ok(()) 190 | } 191 | 192 | /// Generates the `ConstDefaultUnion` implementation for `union` input types. 193 | /// 194 | /// # Note 195 | /// 196 | /// Zero initialize the first field in the union, rest is handled automatically. 197 | fn generate_default_impl_union( 198 | crate_ident: &TokenStream2, 199 | data_union: &syn::DataUnion, 200 | ) -> Result { 201 | let fields_impl = data_union 202 | .fields 203 | .named 204 | .first() 205 | .map(|field| { 206 | let field_span = field.span(); 207 | let field_type = &field.ty; 208 | let field_pos = Literal::usize_unsuffixed(0); 209 | let field_ident = field 210 | .ident 211 | .as_ref() 212 | .map(|ident| quote_spanned!(field_span=> #ident)) 213 | .unwrap_or_else(|| quote_spanned!(field_span=> #field_pos)); 214 | quote_spanned!(field_span=> 215 | #field_ident: <#field_type as #crate_ident::ConstDefault>::DEFAULT 216 | ) 217 | }) 218 | .unwrap(); 219 | Ok(quote! { 220 | Self { 221 | #fields_impl 222 | } 223 | }) 224 | } 225 | 226 | /// Generates `ConstDefault` where bounds for all fields of the input. 227 | fn generate_default_impl_union_where_bounds( 228 | crate_ident: &TokenStream2, 229 | data_union: &syn::DataUnion, 230 | generics: &mut syn::Generics, 231 | ) -> Result<(), syn::Error> { 232 | let where_clause = generics.make_where_clause(); 233 | if let Some(field) = &data_union.fields.named.first() { 234 | let field_type = &field.ty; 235 | where_clause.predicates.push(syn::parse_quote!( 236 | #field_type: #crate_ident::ConstDefault 237 | )) 238 | } 239 | Ok(()) 240 | } 241 | -------------------------------------------------------------------------------- /const_default/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc(html_root_url = "http://docs.rs/const-default/1.0.0")] 2 | #![cfg_attr(not(feature = "std"), no_std)] 3 | #![cfg_attr(feature = "unstable-docs", feature(doc_cfg))] 4 | #![cfg_attr( 5 | all(feature = "unstable", feature = "alloc"), 6 | feature(const_btree_new) 7 | )] 8 | #![cfg_attr( 9 | all(feature = "unstable", feature = "enable-atomics"), 10 | feature(cfg_target_has_atomic) 11 | )] 12 | #![cfg_attr( 13 | feature = "enable-atomics", 14 | allow(clippy::declare_interior_mutable_const) 15 | )] 16 | 17 | #[cfg(feature = "alloc")] 18 | extern crate alloc; 19 | 20 | #[cfg(feature = "derive")] 21 | #[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "derive")))] 22 | pub use const_default_derive::ConstDefault; 23 | 24 | /// Implements a compilation time default value for the implemented type. 25 | /// 26 | /// # Note 27 | /// 28 | /// Unlike the [`Default`] trait implementation the `DEFAULT` of implementations 29 | /// of this trait can be used in constant evaluation contexts. 30 | /// 31 | /// # Example 32 | /// 33 | /// ``` 34 | /// # #[cfg(feature = "std")] 35 | /// # const _: () = { 36 | /// # use const_default::ConstDefault; 37 | /// const VEC: Vec = as ConstDefault>::DEFAULT; 38 | /// # }; 39 | /// ``` 40 | /// 41 | /// The above code works while the below code does not: 42 | /// 43 | /// ```compile_fail 44 | /// const VEC: Vec = as Default>::default(); 45 | /// ``` 46 | pub trait ConstDefault: Sized { 47 | /// The constant default value. 48 | const DEFAULT: Self; 49 | } 50 | 51 | /// Returns the compilation time default value for a type 52 | #[inline] 53 | #[cfg(feature = "unstable")] 54 | #[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "unstable")))] 55 | pub const fn const_default() -> T { 56 | T::DEFAULT 57 | } 58 | 59 | impl ConstDefault for Option { 60 | const DEFAULT: Self = None; 61 | } 62 | 63 | #[cfg(feature = "alloc")] 64 | #[cfg_attr( 65 | feature = "unstable-docs", 66 | doc(cfg(any(feature = "std", feature = "alloc"))) 67 | )] 68 | impl<'a, T> ConstDefault for alloc::borrow::Cow<'a, T> 69 | where 70 | T: alloc::borrow::ToOwned + ?Sized + 'a, 71 | ::Owned: ConstDefault, 72 | { 73 | const DEFAULT: Self = Self::Owned( 74 | <::Owned as ConstDefault>::DEFAULT, 75 | ); 76 | } 77 | 78 | impl ConstDefault for core::cell::Cell { 79 | const DEFAULT: Self = Self::new(T::DEFAULT); 80 | } 81 | 82 | impl ConstDefault for core::cell::UnsafeCell { 83 | const DEFAULT: Self = Self::new(T::DEFAULT); 84 | } 85 | 86 | impl ConstDefault for core::cell::RefCell { 87 | const DEFAULT: Self = Self::new(T::DEFAULT); 88 | } 89 | 90 | // TODO revisit whether this makes sense? 91 | impl ConstDefault for core::mem::MaybeUninit { 92 | const DEFAULT: Self = Self::new(T::DEFAULT); 93 | } 94 | 95 | #[cfg(feature = "alloc")] 96 | #[cfg_attr( 97 | feature = "unstable-docs", 98 | doc(cfg(any(feature = "std", feature = "alloc"))) 99 | )] 100 | impl ConstDefault for alloc::vec::Vec { 101 | const DEFAULT: Self = Self::new(); 102 | } 103 | 104 | #[cfg(feature = "alloc")] 105 | #[cfg_attr( 106 | feature = "unstable-docs", 107 | doc(cfg(any(feature = "std", feature = "alloc"))) 108 | )] 109 | impl ConstDefault for alloc::string::String { 110 | const DEFAULT: Self = Self::new(); 111 | } 112 | 113 | #[cfg(all(feature = "alloc", feature = "unstable"))] 114 | #[cfg_attr( 115 | feature = "unstable-docs", 116 | doc( 117 | cfg(any(feature = "std", feature = "alloc")), 118 | cfg(feature = "unstable") 119 | ) 120 | )] 121 | impl ConstDefault for alloc::collections::BTreeMap { 122 | const DEFAULT: Self = Self::new(); 123 | } 124 | 125 | #[cfg(all(feature = "alloc", feature = "unstable"))] 126 | #[cfg_attr( 127 | feature = "unstable-docs", 128 | doc( 129 | cfg(any(feature = "std", feature = "alloc")), 130 | cfg(feature = "unstable") 131 | ) 132 | )] 133 | impl ConstDefault for alloc::collections::BTreeSet { 134 | const DEFAULT: Self = Self::new(); 135 | } 136 | 137 | #[cfg(feature = "alloc")] 138 | #[cfg_attr( 139 | feature = "unstable-docs", 140 | doc(cfg(any(feature = "std", feature = "alloc"))) 141 | )] 142 | impl ConstDefault for alloc::collections::LinkedList { 143 | const DEFAULT: Self = Self::new(); 144 | } 145 | 146 | impl<'a, T: 'a> ConstDefault for &'a [T] { 147 | const DEFAULT: Self = &[]; 148 | } 149 | 150 | impl ConstDefault for *const T { 151 | const DEFAULT: Self = core::ptr::null(); 152 | } 153 | 154 | impl ConstDefault for *mut T { 155 | const DEFAULT: Self = core::ptr::null_mut(); 156 | } 157 | 158 | impl ConstDefault for core::mem::ManuallyDrop { 159 | const DEFAULT: Self = Self::new(T::DEFAULT); 160 | } 161 | 162 | impl ConstDefault for core::marker::PhantomData { 163 | const DEFAULT: Self = Self; 164 | } 165 | 166 | impl ConstDefault for core::marker::PhantomPinned { 167 | const DEFAULT: Self = Self; 168 | } 169 | 170 | impl ConstDefault for core::iter::Empty { 171 | const DEFAULT: Self = core::iter::empty(); 172 | } 173 | 174 | impl ConstDefault for core::num::Wrapping { 175 | const DEFAULT: Self = Self(T::DEFAULT); 176 | } 177 | 178 | impl ConstDefault for core::time::Duration { 179 | const DEFAULT: Self = core::time::Duration::from_secs(0); 180 | } 181 | 182 | #[cfg(feature = "std")] 183 | #[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))] 184 | impl ConstDefault for std::sync::Once { 185 | const DEFAULT: Self = Self::new(); 186 | } 187 | 188 | macro_rules! impl_num { 189 | ($($ty:ty=$d:expr$(;$name:ident=$width:literal)?),*) => { 190 | $( 191 | impl ConstDefault for $ty { 192 | const DEFAULT: Self = $d; 193 | } 194 | 195 | impl ConstDefault for &$ty { 196 | const DEFAULT: Self = &<$ty as ConstDefault>::DEFAULT; 197 | } 198 | 199 | $( 200 | #[cfg(feature = "enable-atomics")] 201 | #[cfg_attr(feature = "unstable-docs", doc(cfg(any(feature = "default", feature = "enable-atomics"))))] 202 | #[cfg_attr(feature = "unstable", cfg(target_has_atomic = $width))] 203 | impl ConstDefault for core::sync::atomic::$name { 204 | const DEFAULT: Self = Self::new(ConstDefault::DEFAULT); 205 | } 206 | )? 207 | )* 208 | }; 209 | } 210 | 211 | impl_num! { 212 | ()=(), bool=false, f32=0.0, f64=0.0, char='\x00', &str="", 213 | u8=0;AtomicU8="8", u16=0;AtomicU16="16", u32=0;AtomicU32="32", u64=0;AtomicU64="64", usize=0;AtomicUsize="ptr", 214 | i8=0;AtomicI8="8", i16=0;AtomicI16="16", i32=0;AtomicI32="32", i64=0;AtomicI64="64", isize=0;AtomicIsize="ptr", 215 | i128=0, u128=0 216 | } 217 | 218 | #[cfg(feature = "enable-atomics")] 219 | #[cfg_attr( 220 | feature = "unstable-docs", 221 | doc(cfg(any(feature = "default", feature = "enable-atomics"))) 222 | )] 223 | #[cfg_attr(feature = "unstable", cfg(target_has_atomic = "8"))] 224 | impl ConstDefault for core::sync::atomic::AtomicBool { 225 | const DEFAULT: Self = Self::new(ConstDefault::DEFAULT); 226 | } 227 | 228 | #[cfg(feature = "enable-atomics")] 229 | #[cfg_attr( 230 | feature = "unstable-docs", 231 | doc(cfg(any(feature = "default", feature = "enable-atomics"))) 232 | )] 233 | #[cfg_attr(feature = "unstable", cfg(target_has_atomic = "ptr"))] 234 | impl ConstDefault for core::sync::atomic::AtomicPtr { 235 | const DEFAULT: Self = Self::new(core::ptr::null_mut()); 236 | } 237 | 238 | macro_rules! impl_tuple { 239 | (@rec $t:ident) => { }; 240 | (@rec $_:ident $($t:ident)+) => { 241 | impl_tuple! { @impl $($t)* } 242 | impl_tuple! { @rec $($t)* } 243 | }; 244 | (@impl $($t:ident)*) => { 245 | impl<$($t: ConstDefault,)*> ConstDefault for ($($t,)*) { 246 | const DEFAULT: Self = ($($t::DEFAULT,)*); 247 | } 248 | }; 249 | ($($t:ident)*) => { 250 | impl_tuple! { @rec _t $($t)* } 251 | }; 252 | } 253 | 254 | impl_tuple! { 255 | A B C D E F G H I J K L 256 | } 257 | 258 | impl ConstDefault for [T; N] { 259 | const DEFAULT: Self = [T::DEFAULT; N]; 260 | } 261 | -------------------------------------------------------------------------------- /const_default/tests/derive.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "derive")] 2 | 3 | use const_default::ConstDefault; 4 | use core::cell::{Cell, RefCell}; 5 | #[cfg(feature = "enable-atomics")] 6 | use core::sync::atomic::{ 7 | AtomicBool, 8 | AtomicI16, 9 | AtomicI32, 10 | AtomicI64, 11 | AtomicI8, 12 | AtomicIsize, 13 | AtomicPtr, 14 | AtomicU16, 15 | AtomicU32, 16 | AtomicU64, 17 | AtomicU8, 18 | AtomicUsize, 19 | Ordering, 20 | }; 21 | 22 | #[test] 23 | fn struct_of_primitives_works() { 24 | #[derive(ConstDefault, Debug, Default, PartialEq)] 25 | pub struct TestType { 26 | field_0: bool, 27 | field_1: i8, 28 | field_2: i16, 29 | field_3: i32, 30 | field_4: i64, 31 | field_5: i128, 32 | field_6: isize, 33 | field_7: u8, 34 | field_8: u16, 35 | field_9: u32, 36 | field_10: u64, 37 | field_11: u128, 38 | field_12: usize, 39 | field_13: char, 40 | } 41 | assert_eq!(::DEFAULT, TestType::default()); 42 | } 43 | 44 | #[test] 45 | fn tuple_struct_of_primitives_works() { 46 | #[derive(ConstDefault, Debug, Default, PartialEq)] 47 | pub struct TestType( 48 | bool, 49 | i8, 50 | i16, 51 | i32, 52 | i64, 53 | i128, 54 | isize, 55 | u8, 56 | u16, 57 | u32, 58 | u64, 59 | u128, 60 | usize, 61 | char, 62 | ); 63 | assert_eq!(::DEFAULT, TestType::default()); 64 | } 65 | 66 | #[test] 67 | #[cfg(feature = "enable-atomics")] 68 | fn struct_of_atomic_primitives_works() { 69 | #[derive(ConstDefault, Debug, Default)] 70 | pub struct TestType { 71 | field_1: AtomicBool, 72 | field_2: AtomicI8, 73 | field_3: AtomicI16, 74 | field_4: AtomicI32, 75 | field_5: AtomicI64, 76 | field_6: AtomicIsize, 77 | field_7: AtomicU8, 78 | field_8: AtomicU16, 79 | field_9: AtomicU32, 80 | field_10: AtomicU64, 81 | field_11: AtomicUsize, 82 | field_12: AtomicPtr, 83 | } 84 | let t1 = ::DEFAULT; 85 | let t2 = TestType::default(); 86 | use Ordering::SeqCst as O; 87 | assert_eq!(t1.field_1.load(O), t2.field_1.load(O)); 88 | assert_eq!(t1.field_2.load(O), t2.field_2.load(O)); 89 | assert_eq!(t1.field_3.load(O), t2.field_3.load(O)); 90 | assert_eq!(t1.field_4.load(O), t2.field_4.load(O)); 91 | assert_eq!(t1.field_5.load(O), t2.field_5.load(O)); 92 | assert_eq!(t1.field_6.load(O), t2.field_6.load(O)); 93 | assert_eq!(t1.field_7.load(O), t2.field_7.load(O)); 94 | assert_eq!(t1.field_8.load(O), t2.field_8.load(O)); 95 | assert_eq!(t1.field_9.load(O), t2.field_9.load(O)); 96 | assert_eq!(t1.field_10.load(O), t2.field_10.load(O)); 97 | assert_eq!(t1.field_11.load(O), t2.field_11.load(O)); 98 | assert_eq!(t1.field_12.load(O), t2.field_12.load(O)); 99 | } 100 | 101 | #[test] 102 | #[cfg(feature = "enable-atomics")] 103 | fn tuple_struct_of_atomic_primitives_works() { 104 | #[derive(ConstDefault, Debug, Default)] 105 | pub struct TestType( 106 | AtomicBool, 107 | AtomicI8, 108 | AtomicI16, 109 | AtomicI32, 110 | AtomicI64, 111 | AtomicIsize, 112 | AtomicU8, 113 | AtomicU16, 114 | AtomicU32, 115 | AtomicU64, 116 | AtomicUsize, 117 | AtomicPtr, 118 | ); 119 | let t1 = ::DEFAULT; 120 | let t2 = TestType::default(); 121 | use Ordering::SeqCst as O; 122 | assert_eq!(t1.0.load(O), t2.0.load(O)); 123 | assert_eq!(t1.1.load(O), t2.1.load(O)); 124 | assert_eq!(t1.2.load(O), t2.2.load(O)); 125 | assert_eq!(t1.3.load(O), t2.3.load(O)); 126 | assert_eq!(t1.4.load(O), t2.4.load(O)); 127 | assert_eq!(t1.5.load(O), t2.5.load(O)); 128 | assert_eq!(t1.6.load(O), t2.6.load(O)); 129 | assert_eq!(t1.7.load(O), t2.7.load(O)); 130 | assert_eq!(t1.8.load(O), t2.8.load(O)); 131 | assert_eq!(t1.9.load(O), t2.9.load(O)); 132 | assert_eq!(t1.10.load(O), t2.10.load(O)); 133 | assert_eq!(t1.11.load(O), t2.11.load(O)); 134 | } 135 | 136 | #[test] 137 | fn struct_of_structs_works() { 138 | #[derive(ConstDefault, Debug, Default, PartialEq)] 139 | pub struct OuterStruct { 140 | field_1: InnerStruct, 141 | field_2: InnerStruct, 142 | } 143 | #[derive(ConstDefault, Debug, Default, PartialEq)] 144 | pub struct InnerStruct { 145 | field_1: i32, 146 | field_2: u32, 147 | } 148 | assert_eq!( 149 | ::DEFAULT, 150 | OuterStruct::default() 151 | ); 152 | } 153 | 154 | #[test] 155 | fn tuple_struct_of_structs_works() { 156 | #[derive(ConstDefault, Debug, Default, PartialEq)] 157 | pub struct OuterStruct(InnerStruct, InnerStruct); 158 | #[derive(ConstDefault, Debug, Default, PartialEq)] 159 | pub struct InnerStruct(i32, u32); 160 | assert_eq!( 161 | ::DEFAULT, 162 | OuterStruct::default() 163 | ); 164 | } 165 | 166 | #[test] 167 | fn struct_of_cell_types_works() { 168 | #[derive(ConstDefault, Debug, Default, PartialEq)] 169 | pub struct TestStruct { 170 | field_1: Cell, 171 | field_2: RefCell, 172 | } 173 | } 174 | 175 | #[test] 176 | fn type_alias_works() { 177 | type TestAlias = i32; 178 | 179 | #[derive(ConstDefault, Debug, Default, PartialEq)] 180 | pub struct TestType1 { 181 | field_0: TestAlias, 182 | } 183 | #[derive(ConstDefault, Debug, Default, PartialEq)] 184 | pub struct TestType2(TestAlias); 185 | assert_eq!(::DEFAULT, TestType1::default()); 186 | assert_eq!(::DEFAULT, TestType2::default()); 187 | } 188 | -------------------------------------------------------------------------------- /const_default/tests/trait-impls.rs: -------------------------------------------------------------------------------- 1 | use const_default::ConstDefault; 2 | #[cfg(feature = "enable-atomics")] 3 | use core::sync::atomic::{ 4 | AtomicBool, 5 | AtomicI16, 6 | AtomicI32, 7 | AtomicI64, 8 | AtomicI8, 9 | AtomicIsize, 10 | AtomicPtr, 11 | AtomicU16, 12 | AtomicU32, 13 | AtomicU64, 14 | AtomicU8, 15 | AtomicUsize, 16 | Ordering, 17 | }; 18 | use core::{ 19 | cell::{Cell, RefCell}, 20 | fmt::Debug, 21 | }; 22 | 23 | /// Checks if both `ConstDefault` and `Default` implementations yield the same outcome. 24 | fn compare_default_impls() 25 | where 26 | T: ConstDefault + Default + PartialEq + Debug, 27 | { 28 | assert_eq!(::DEFAULT, ::default()); 29 | } 30 | 31 | macro_rules! compare_default_impls_for { 32 | ( $( $ty:ty ),* $(,)? ) => {{ 33 | $( 34 | compare_default_impls::<$ty>(); 35 | )* 36 | }}; 37 | } 38 | 39 | #[test] 40 | fn primitive_impls_work() { 41 | #[rustfmt::skip] 42 | compare_default_impls_for!( 43 | bool, char, 44 | i8, i16, i32, i64, i128, isize, 45 | u8, u16, u32, u64, u128, usize, 46 | ); 47 | } 48 | 49 | #[test] 50 | fn tuple_impls_work() { 51 | #[rustfmt::skip] 52 | compare_default_impls_for!( 53 | (), 54 | (i8,), 55 | (i8, i16), 56 | (i8, i16, i32), 57 | (i8, i16, i32, i64), 58 | (i8, i16, i32, i64, i128), 59 | (i8, i16, i32, i64, i128, isize), 60 | (i8, i16, i32, i64, i128, isize, u8), 61 | (i8, i16, i32, i64, i128, isize, u8, u16), 62 | (i8, i16, i32, i64, i128, isize, u8, u16, u32), 63 | (i8, i16, i32, i64, i128, isize, u8, u16, u32, u64), 64 | (i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128), 65 | (i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize), 66 | ); 67 | } 68 | 69 | macro_rules! compare_default_impls_for_arrays { 70 | ( $( $n:literal ),* $(,)? ) => {{ 71 | $( 72 | compare_default_impls::<[(); $n]>(); 73 | )* 74 | }}; 75 | } 76 | 77 | #[test] 78 | fn array_impls_work() { 79 | #[rustfmt::skip] 80 | compare_default_impls_for_arrays!( 81 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 82 | 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 83 | 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 84 | 30, 31, 32, 85 | ); 86 | } 87 | 88 | #[cfg(feature = "enable-atomics")] 89 | macro_rules! compare_default_impls_for_atomics { 90 | ( $( $atomic_type:ty ),* $(,)? ) => {{ 91 | $( 92 | assert_eq!( 93 | <$atomic_type as ConstDefault>::DEFAULT.load(Ordering::SeqCst), 94 | <$atomic_type as Default>::default().load(Ordering::SeqCst), 95 | ); 96 | )* 97 | }}; 98 | } 99 | 100 | #[test] 101 | #[cfg(feature = "enable-atomics")] 102 | fn atomic_impls_work() { 103 | compare_default_impls_for_atomics!( 104 | AtomicBool, 105 | AtomicI16, 106 | AtomicI32, 107 | AtomicI64, 108 | AtomicI8, 109 | AtomicIsize, 110 | AtomicU16, 111 | AtomicU32, 112 | AtomicU64, 113 | AtomicU8, 114 | AtomicUsize, 115 | ); 116 | assert_eq!( 117 | as ConstDefault>::DEFAULT.load(Ordering::SeqCst), 118 | as Default>::default().load(Ordering::SeqCst), 119 | ); 120 | } 121 | 122 | macro_rules! compare_default_impls_for_cells { 123 | ( $( $cell_type:ty ),* $(,)? ) => {{ 124 | $( 125 | assert_eq!( 126 | <$cell_type as ConstDefault>::DEFAULT.into_inner(), 127 | <$cell_type as Default>::default().into_inner(), 128 | ); 129 | )* 130 | }}; 131 | } 132 | 133 | #[test] 134 | fn cell_impls_work() { 135 | #[rustfmt::skip] 136 | compare_default_impls_for_cells!( 137 | Cell, 138 | Cell<(u8, u16, u32)>, 139 | Cell>, 140 | RefCell, 141 | RefCell<(u8, u16, u32)>, 142 | RefCell>, 143 | ); 144 | } 145 | -------------------------------------------------------------------------------- /include/constants.h: -------------------------------------------------------------------------------- 1 | #ifndef CONSTANTS_H 2 | #define CONSTANTS_H 3 | 4 | #include "func.h" 5 | 6 | #define ALIGNED(a) __attribute__ ((aligned (a))) 7 | 8 | #define PATH_MAX (0x100) 9 | 10 | #define LOCAL_TITLE_BUF_SIZE (0x80) 11 | #define LOCAL_MSG_BUF_SIZE (0x200) 12 | #define LOCAL_OPT_TEXT_BUF_SIZE (0x40) 13 | #define LOCAL_TID_BUF_COUNT (0x80) 14 | #define LOCAL_PID_BUF_COUNT (0x100) 15 | 16 | #define DBG_VERBOSE_TITLE "[%"PRId32".%06"PRId32"][%"PRIx32"]%s:%d:%s" 17 | 18 | // Require Luma3DS PA-VA mapping 19 | static const u32 IoBaseLcd = 0x10202000 + 0x80000000; 20 | static const u32 IoBasePad = 0x10146000 + 0x80000000; 21 | static const u32 IoBasePdc = 0x10400000 + 0x80000000; 22 | 23 | #define DIRECTIONAL_KEYS (KEY_DOWN | KEY_UP | KEY_LEFT | KEY_RIGHT) 24 | 25 | #define REG32(reg) (*(vu32 *)reg) 26 | // From Luma3DS 27 | #define GPU_FB_TOP_SIZE (IoBasePdc + 0x45c) 28 | #define GPU_FB_TOP_LEFT_ADDR_1 (IoBasePdc + 0x468) 29 | #define GPU_FB_TOP_LEFT_ADDR_2 (IoBasePdc + 0x46C) 30 | #define GPU_FB_TOP_FMT (IoBasePdc + 0x470) 31 | #define GPU_FB_TOP_SEL (IoBasePdc + 0x478) 32 | #define GPU_FB_TOP_COL_LUT_INDEX (IoBasePdc + 0x480) 33 | #define GPU_FB_TOP_COL_LUT_ELEM (IoBasePdc + 0x484) 34 | #define GPU_FB_TOP_STRIDE (IoBasePdc + 0x490) 35 | #define GPU_FB_TOP_RIGHT_ADDR_1 (IoBasePdc + 0x494) 36 | #define GPU_FB_TOP_RIGHT_ADDR_2 (IoBasePdc + 0x498) 37 | 38 | #define GPU_FB_BOTTOM_SIZE (IoBasePdc + 0x55c) 39 | #define GPU_FB_BOTTOM_ADDR_1 (IoBasePdc + 0x568) 40 | #define GPU_FB_BOTTOM_ADDR_2 (IoBasePdc + 0x56C) 41 | #define GPU_FB_BOTTOM_FMT (IoBasePdc + 0x570) 42 | #define GPU_FB_BOTTOM_SEL (IoBasePdc + 0x578) 43 | #define GPU_FB_BOTTOM_COL_LUT_INDEX (IoBasePdc + 0x580) 44 | #define GPU_FB_BOTTOM_COL_LUT_ELEM (IoBasePdc + 0x584) 45 | #define GPU_FB_BOTTOM_STRIDE (IoBasePdc + 0x590) 46 | 47 | #define GPU_PSC0_CNT (IoBasePdc + 0x01C) 48 | #define GPU_PSC1_CNT (IoBasePdc + 0x02C) 49 | 50 | #define GPU_TRANSFER_CNT (IoBasePdc + 0xC18) 51 | #define GPU_CMDLIST_CNT (IoBasePdc + 0x18F0) 52 | 53 | #define LCD_TOP_BRIGHTNESS (IoBaseLcd + 0x240) 54 | #define LCD_TOP_FILLCOLOR (IoBaseLcd + 0x204) 55 | #define LCD_BOT_BRIGHTNESS (IoBaseLcd + 0xA40) 56 | #define LCD_BOT_FILLCOLOR (IoBaseLcd + 0xA04) 57 | 58 | #define PDN_LGR_SOCMODE (0x10141300 + 0x80000000) 59 | 60 | #define COPY_REMOTE_MEMORY_TIMEOUT (-1) 61 | #define PM_INIT_READY_TIMEOUT (-1) 62 | #define NWM_INIT_READY_TIMEOUT (-1) 63 | 64 | #define NS_CONFIG_ADDR (0x06000000) 65 | #define NS_CONFIG_MAX_SIZE (0x1000) 66 | #define NS_SOC_ADDR (0x06f00000) 67 | #define NS_SOC_SHARED_BUF_SIZE (0x10000) 68 | #define NS_CTX_ADDR (NS_SOC_ADDR + STACK_SIZE) 69 | 70 | #define PLG_POOL_ADDR (0x07000000) 71 | #define PLG_MEM_ADDR (0x06200000) 72 | 73 | #define PROC_START_ADDR (0x00100000) 74 | 75 | #define NS_MENU_LISTEN_PORT (8000) 76 | #define NS_HOOK_LISTEN_PORT (5000) 77 | // Manual RP init from NTR menu 78 | // SRC is 3DS, DST is PC 79 | // Intentionally different from the RP default to avoid sending 80 | // garbage to unsuspecting viewer 81 | #define NWM_INIT_SRC_PORT (8001) 82 | #define NWM_INIT_DST_PORT (8000) 83 | // RP data ports 84 | #define RP_SRC_PORT (8000) 85 | #define RP_DST_PORT_DEFAULT (8001) 86 | #define RP_THREAD_PRIO_DEFAULT RP_THREAD_PRIO_MAX 87 | #define RP_CORE_COUNT_MIN (1) 88 | #define RP_CORE_COUNT_MAX (3) 89 | #define RP_QUALITY_DEFAULT (75) 90 | #define RP_QUALITY_MIN (10) 91 | #define RP_QUALITY_MAX (100) 92 | // 2.0 MBps or 16 Mbps 93 | #define RP_QOS_DEFAULT (2 * 1024 * 1024) 94 | // 0.5 MBps or 4 Mbps 95 | #define RP_QOS_MIN (1 * 1024 * 1024 / 2) 96 | // 2.5 MBps or 20 Mbps 97 | #define RP_QOS_MAX (5 * 1024 * 1024 / 2) 98 | #define RP_PORT_MIN (1024) 99 | #define RP_PORT_MAX (65535) 100 | #define RP_THREAD_PRIO_MIN (0x10) 101 | #define RP_THREAD_PRIO_MAX (0x3f) 102 | #define RP_CORE_COUNT_DEFAULT RP_CORE_COUNT_MAX 103 | 104 | #define NWM_HDR_SIZE (0x2a + 8) 105 | #define DATA_HDR_SIZE (4) 106 | #define PACKET_SIZE (1448) 107 | 108 | #define DEBUG_BUF_SIZE (0x2000) 109 | 110 | #define SMALL_STACK_SIZE (0x1000) 111 | #define STACK_SIZE (0x4000) 112 | #define RP_THREAD_STACK_SIZE (0x10000) 113 | 114 | #define DBG_CL_FATAL (0x10000ff) 115 | #define DBG_CL_MSG (DBG_CL_FATAL) 116 | #define DBG_CL_INFO (0x1ff0000) 117 | #define DBG_CL_USE_DBG (0x17f7f7f) 118 | #define DBG_CL_USE_DBG_FAIL (0xff00ff) 119 | #define DBG_CL_USE_INJECT (0x100ff00) 120 | 121 | #define RES_HANDLE_CLOSED (0xC920181A) 122 | #define RES_TIMEOUT (0x09401BFE) 123 | 124 | #define SVC_PORT_NWM "nwm:rp" 125 | #define SVC_PORT_MENU "menu:ns" 126 | 127 | enum { 128 | SVC_NWM_CMD_OVERLAY_CALLBACK = 1, 129 | SVC_NWM_CMD_PARAMS_UPDATE, 130 | SVC_NWM_CMD_GAME_PID_UPDATE, 131 | }; 132 | 133 | enum { 134 | SVC_MENU_CMD_DBG_PRINT = 1, 135 | SVC_MENU_CMD_SHOW_MSG, 136 | }; 137 | 138 | enum { 139 | RP_CHROMASS_420, 140 | RP_CHROMASS_422, 141 | RP_CHROMASS_444, 142 | RP_CHROMASS_MIN = RP_CHROMASS_420, 143 | RP_CHROMASS_MAX = RP_CHROMASS_444, 144 | }; 145 | 146 | enum { 147 | RP_SCREEN_TOP, 148 | RP_SCREEN_BOT, 149 | RP_SCREEN_COUNT, 150 | }; 151 | 152 | #define RP_DELTA_Q_COEFS_COUNT (3) 153 | 154 | #define NWM_HEAP_SIZE (0x4000) 155 | #define NWM_WORK_COUNT (2) 156 | #define NWM_THREAD_WAIT_NS (100000000) 157 | 158 | #define RP_COMPRESSED_SIZE_MAX (0x30000) 159 | 160 | #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) 161 | #define ROUND_UP(n, d) (DIV_ROUND_UP(n, d) * (d)) 162 | 163 | #define ARQ_DATA_SIZE (PACKET_SIZE - ARQ_OVERHEAD_SIZE) 164 | #define ARQ_DATA_HDR_SIZE 2 165 | #define ARQ_RP_DATA_SIZE (ARQ_DATA_SIZE - ARQ_DATA_HDR_SIZE) 166 | #define RP_DATA_SIZE (PACKET_SIZE - DATA_HDR_SIZE) 167 | #define RP_COMPRESSED_COUNT_MAX (DIV_ROUND_UP(RP_COMPRESSED_SIZE_MAX, RP_DATA_SIZE) * NWM_WORK_COUNT) 168 | #define RP_QOS_PACKET_RATE_MAX (DIV_ROUND_UP(RP_QOS_MAX, PACKET_SIZE)) 169 | // 250 ms or 1/4 of a second of buffered packets 170 | #define ARQ_PREFERRED_BUFFER_DURATION_FACTOR (4) 171 | #define ARQ_PREFERRED_COUNT_MAX DIV_ROUND_UP(RP_QOS_PACKET_RATE_MAX, ARQ_PREFERRED_BUFFER_DURATION_FACTOR) 172 | // 25 ms or 1/40 of a second of queued packets for send 173 | #define ARQ_CUR_COUNT_MAX DIV_ROUND_UP(RP_QOS_PACKET_RATE_MAX, 40) 174 | // Additional allocable count is multiplied by max recovery to original ratio for FEC 175 | #define ARQ_CUR_COUNT_MAX_2 DIV_ROUND_UP(ARQ_PREFERRED_COUNT_MAX, 2) 176 | // Additional allocable count is multiplied by max recovery to original ratio 177 | #define ARQ_PREFERRED_COUNT_MAX_2 (ARQ_PREFERRED_COUNT_MAX * 2) 178 | // Additional count for buffer room for encoding 179 | #define RP_ARQ_ENCODE_COUNT (RP_CORE_COUNT_MAX * NWM_WORK_COUNT) 180 | #define RP_ARQ_ENCODE_COUNT_MAX (ARQ_PREFERRED_COUNT_MAX + RP_ARQ_ENCODE_COUNT) 181 | // Additional count for finalizing frames 182 | #define RP_FRAME_RATE_EXPECTED_MAX (120) 183 | #define RP_ARQ_TERM_COUNT ((RP_CORE_COUNT_MAX + 1) * DIV_ROUND_UP(RP_FRAME_RATE_EXPECTED_MAX, ARQ_PREFERRED_BUFFER_DURATION_FACTOR)) 184 | #define RP_ARQ_PREFERRED_COUNT_MAX (RP_ARQ_ENCODE_COUNT_MAX + RP_ARQ_TERM_COUNT) 185 | // Includes FEC_OVERHEAD_SIZE 186 | #define ARQ_OVERHEAD_SIZE 2 187 | #define ARQ_SEG_SIZE (sizeof(struct IKCPSEG)) 188 | 189 | #define FEC_OVERHEAD_SIZE 2 190 | #define FEC_DATA_SIZE (PACKET_SIZE - FEC_OVERHEAD_SIZE) 191 | 192 | _Static_assert((NWM_HDR_SIZE + FEC_OVERHEAD_SIZE) % sizeof(void *) == 0, "Need adjusting overhead for alignment."); 193 | _Static_assert(RP_DATA_SIZE % sizeof(void *) == 0, "Need adjusting packet size for alignment."); 194 | 195 | #define SEND_BUFS_DATA_COUNT MAX(RP_COMPRESSED_COUNT_MAX, RP_ARQ_PREFERRED_COUNT_MAX) 196 | 197 | #define RP_CONFIG_RELIABLE_STREAM_FLAG (1 << 30) 198 | #define RP_CONFIG_RELIABLE_STREAM_DELTA_PROG (1 << 31) 199 | 200 | #define RP_KCP_HDR_QUALITY_NBITS (7) 201 | #define RP_KCP_HDR_CHROMASS_NBITS (2) 202 | 203 | #endif 204 | -------------------------------------------------------------------------------- /include/font.h: -------------------------------------------------------------------------------- 1 | // Font: READABLE.pf 2 | 3 | #define CHAR_WIDTH (8) 4 | #define CHAR_HEIGHT (12) 5 | 6 | static unsigned char font[] = 7 | { 8 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 9 | 0x10, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x6C, 0x48, 0x48, 0x00, 0x00, 0x00, 0x00, 10 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x14, 0x28, 0x7C, 0x28, 0x7C, 0x28, 0x50, 0x50, 0x00, 0x00, 11 | 0x00, 0x10, 0x38, 0x40, 0x40, 0x38, 0x48, 0x70, 0x10, 0x10, 0x00, 0x00, 0x00, 0x20, 0x50, 0x20, 12 | 0x0C, 0x70, 0x08, 0x14, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x20, 0x20, 0x54, 0x48, 13 | 0x34, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 14 | 0x00, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x08, 0x00, 0x00, 0x20, 0x20, 0x10, 15 | 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x00, 0x00, 0x10, 0x7C, 0x10, 0x28, 0x28, 0x00, 0x00, 16 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0xFC, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 17 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x10, 0x30, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 18 | 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 19 | 0x30, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x40, 0x00, 0x00, 20 | 0x00, 0x38, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, 0x00, 0x30, 0x10, 0x10, 21 | 0x10, 0x10, 0x10, 0x10, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x04, 0x08, 0x10, 0x20, 0x44, 22 | 0x7C, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x04, 0x18, 0x04, 0x04, 0x44, 0x38, 0x00, 0x00, 0x00, 23 | 0x00, 0x0C, 0x14, 0x14, 0x24, 0x44, 0x7C, 0x04, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x20, 0x20, 24 | 0x38, 0x04, 0x04, 0x44, 0x38, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x40, 0x78, 0x44, 0x44, 0x44, 25 | 0x38, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x44, 0x04, 0x08, 0x08, 0x08, 0x10, 0x10, 0x00, 0x00, 0x00, 26 | 0x00, 0x38, 0x44, 0x44, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x44, 27 | 0x44, 0x3C, 0x04, 0x08, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 28 | 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x30, 0x20, 0x00, 0x00, 29 | 0x00, 0x00, 0x0C, 0x10, 0x60, 0x80, 0x60, 0x10, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 30 | 0x7C, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x20, 0x18, 0x04, 0x18, 0x20, 31 | 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x04, 0x08, 0x10, 0x00, 0x30, 0x00, 0x00, 0x00, 32 | 0x38, 0x44, 0x44, 0x4C, 0x54, 0x54, 0x4C, 0x40, 0x44, 0x38, 0x00, 0x00, 0x00, 0x30, 0x10, 0x28, 33 | 0x28, 0x28, 0x7C, 0x44, 0xEC, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x44, 0x44, 0x78, 0x44, 0x44, 0x44, 34 | 0xF8, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x44, 0x40, 0x40, 0x40, 0x40, 0x44, 0x38, 0x00, 0x00, 0x00, 35 | 0x00, 0xF0, 0x48, 0x44, 0x44, 0x44, 0x44, 0x48, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x44, 0x50, 36 | 0x70, 0x50, 0x40, 0x44, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x20, 0x28, 0x38, 0x28, 0x20, 0x20, 37 | 0x70, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x44, 0x40, 0x40, 0x4C, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, 38 | 0x00, 0xEC, 0x44, 0x44, 0x7C, 0x44, 0x44, 0x44, 0xEC, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x10, 0x10, 39 | 0x10, 0x10, 0x10, 0x10, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x08, 0x08, 0x08, 0x48, 0x48, 0x48, 40 | 0x30, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x44, 0x48, 0x50, 0x70, 0x48, 0x44, 0xE4, 0x00, 0x00, 0x00, 41 | 0x00, 0x70, 0x20, 0x20, 0x20, 0x20, 0x24, 0x24, 0x7C, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x6C, 0x6C, 42 | 0x54, 0x54, 0x44, 0x44, 0xEC, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x64, 0x64, 0x54, 0x54, 0x54, 0x4C, 43 | 0xEC, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, 44 | 0x00, 0x78, 0x24, 0x24, 0x24, 0x38, 0x20, 0x20, 0x70, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x44, 45 | 0x44, 0x44, 0x44, 0x44, 0x38, 0x1C, 0x00, 0x00, 0x00, 0xF8, 0x44, 0x44, 0x44, 0x78, 0x48, 0x44, 46 | 0xE0, 0x00, 0x00, 0x00, 0x00, 0x34, 0x4C, 0x40, 0x38, 0x04, 0x04, 0x64, 0x58, 0x00, 0x00, 0x00, 47 | 0x00, 0xFC, 0x90, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x44, 0x44, 48 | 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x44, 0x44, 0x28, 0x28, 0x28, 0x10, 49 | 0x10, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x44, 0x44, 0x54, 0x54, 0x54, 0x54, 0x28, 0x00, 0x00, 0x00, 50 | 0x00, 0xC4, 0x44, 0x28, 0x10, 0x10, 0x28, 0x44, 0xC4, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x44, 0x28, 51 | 0x28, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x44, 0x08, 0x10, 0x10, 0x20, 0x44, 52 | 0x7C, 0x00, 0x00, 0x00, 0x00, 0x38, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x38, 0x00, 53 | 0x00, 0x40, 0x20, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x38, 0x08, 0x08, 54 | 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x10, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 55 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 56 | 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 57 | 0x44, 0x3C, 0x44, 0x44, 0x3C, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x40, 0x58, 0x64, 0x44, 0x44, 0x44, 58 | 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x44, 0x40, 0x40, 0x44, 0x38, 0x00, 0x00, 0x00, 59 | 0x00, 0x0C, 0x04, 0x34, 0x4C, 0x44, 0x44, 0x44, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 60 | 0x44, 0x7C, 0x40, 0x40, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x7C, 0x20, 0x20, 0x20, 0x20, 61 | 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x4C, 0x44, 0x44, 0x44, 0x3C, 0x04, 0x38, 0x00, 62 | 0x00, 0xC0, 0x40, 0x58, 0x64, 0x44, 0x44, 0x44, 0xEC, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x70, 63 | 0x10, 0x10, 0x10, 0x10, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x78, 0x08, 0x08, 0x08, 0x08, 64 | 0x08, 0x08, 0x70, 0x00, 0x00, 0xC0, 0x40, 0x5C, 0x48, 0x70, 0x50, 0x48, 0xDC, 0x00, 0x00, 0x00, 65 | 0x00, 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 66 | 0x54, 0x54, 0x54, 0x54, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x64, 0x44, 0x44, 0x44, 67 | 0xEC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, 68 | 0x00, 0x00, 0x00, 0xD8, 0x64, 0x44, 0x44, 0x44, 0x78, 0x40, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x34, 69 | 0x4C, 0x44, 0x44, 0x44, 0x3C, 0x04, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x6C, 0x30, 0x20, 0x20, 0x20, 70 | 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x44, 0x38, 0x04, 0x44, 0x78, 0x00, 0x00, 0x00, 71 | 0x00, 0x00, 0x20, 0x7C, 0x20, 0x20, 0x20, 0x20, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 72 | 0x44, 0x44, 0x44, 0x4C, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x44, 0x44, 0x28, 0x28, 73 | 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x44, 0x54, 0x54, 0x54, 0x28, 0x00, 0x00, 0x00, 74 | 0x00, 0x00, 0x00, 0xCC, 0x48, 0x30, 0x30, 0x48, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 75 | 0x44, 0x24, 0x28, 0x18, 0x10, 0x10, 0x78, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x48, 0x10, 0x20, 0x44, 76 | 0x7C, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x10, 0x10, 0x10, 0x20, 0x10, 0x10, 0x10, 0x08, 0x00, 77 | 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x20, 0x10, 0x10, 78 | 0x10, 0x10, 0x08, 0x10, 0x10, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x58, 0x00, 79 | 0x00, 0x00, 0x00, 0x00 80 | }; 81 | -------------------------------------------------------------------------------- /include/func.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNC_H 2 | #define FUNC_H 3 | 4 | #include "3ds/types.h" 5 | 6 | #define REG(x) (*(volatile u32*)(x)) 7 | #define REG8(x) (*(volatile u8*)(x)) 8 | #define REG16(x) (*(volatile u16*)(x)) 9 | 10 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 11 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 12 | #define CLAMP(v, a, b) MAX(MIN((v), (b)), (a)) 13 | #define CWRAP(v, a, b) ((v) < (a) ? (b) : (v) > (b) ? (a) : (v)) 14 | 15 | #define ALIGN_TO_PAGE_SIZE(size) ((size) == 0 ? 0 : ((((size) - 1) / 0x1000) + 1) * 0x1000) 16 | #define PAGE_OF_ADDR(addr) ((addr) / 0x1000 * 0x1000) 17 | 18 | #define AFAR(p, n) __atomic_fetch_add((p), (n), __ATOMIC_RELAXED) 19 | #define AAFR(p, n) __atomic_add_fetch((p), (n), __ATOMIC_RELAXED) 20 | #define ASFR(p, n) __atomic_sub_fetch((p), (n), __ATOMIC_RELAXED) 21 | 22 | #define ATSR(p) __atomic_test_and_set((p), __ATOMIC_RELAXED) 23 | #define ACR(p) __atomic_clear((p), __ATOMIC_RELAXED) 24 | 25 | #define ALC(p) __atomic_load_n((p), __ATOMIC_CONSUME) 26 | #define ASL(p, n) __atomic_store_n((p), (n), __ATOMIC_RELEASE) 27 | 28 | #define ALR(p) __atomic_load_n((p), __ATOMIC_RELAXED) 29 | #define ASR(p, n) __atomic_store_n((p), (n), __ATOMIC_RELAXED) 30 | 31 | #define ATSC(p) __atomic_test_and_set((p), __ATOMIC_CONSUME) 32 | #define ACL(p) __atomic_clear((p), __ATOMIC_RELEASE) 33 | 34 | #define getKeys() ((REG(IoBasePad) & 0xFFF) ^ 0xFFF) 35 | #define canUseUI() (ALC(&hasDirectScreenAccess)) 36 | 37 | void memcpy_ctr(void* dst, void* src, size_t size); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /include/global.h: -------------------------------------------------------------------------------- 1 | #define NTR_CFW_VERSION "NTR 3.6 HR" 2 | 3 | #include "main.h" 4 | #include "rt.h" 5 | #include "ui.h" 6 | #include "svc7b.h" 7 | #include "proc.h" 8 | #include "ns.h" 9 | #include "init.h" 10 | #include "rp.h" 11 | 12 | #include "ntr_config.h" 13 | 14 | #include "xprintf.h" 15 | #include "constants.h" 16 | #include "func.h" 17 | 18 | #include "3ds/types.h" 19 | #include "3ds/svc.h" 20 | #include "3ds/os.h" 21 | #include "csvc.h" 22 | 23 | #include 24 | -------------------------------------------------------------------------------- /include/init.h: -------------------------------------------------------------------------------- 1 | #ifndef INIT_H 2 | #define INIT_H 3 | 4 | #include "ntr_config.h" 5 | int setUpReturn(void); 6 | void startupInit(void); 7 | void loadParams(NTR_CONFIG *ntrCfg); 8 | void initSharedFunc(void); 9 | int plgLoaderInfoAlloc(void); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /include/main.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_H 2 | #define MAIN_H 3 | 4 | #include "3ds/types.h" 5 | #include "ntr_config.h" 6 | #include "ns.h" 7 | 8 | static u32 const *const oldPC = &nsConfig->startupInfo[2]; 9 | int setUpReturn2(void); 10 | void mainThread(void *); 11 | void mainPre(void); 12 | void mainPost(void); 13 | int main(void); 14 | 15 | static NTR_CONFIG *const ntrConfig = &nsConfig->ntrConfig; 16 | 17 | typedef void (*showDbgFunc_t)(const char *); 18 | extern showDbgFunc_t showDbgFunc; 19 | int showMsgDbgFunc(const char *msg); 20 | 21 | extern u32 arm11BinStart; 22 | extern u32 arm11BinSize; 23 | int loadPayloadBin(char *name); 24 | void unloadPayloadBin(void); 25 | 26 | int plgEnsurePoolSize(u32 size); 27 | u32 plgRequestMemory(u32 size); 28 | u32 plgRequestMemoryFromPool(u32 size, int pool); 29 | u32 plgGetMemoryUsage(void); 30 | 31 | void setCpuClockLock(int v, int lock); 32 | 33 | #define MAX_PLUGIN_COUNT 32 34 | typedef struct { 35 | u32 plgCount; 36 | u32 plgBufferPtr[MAX_PLUGIN_COUNT]; 37 | u32 plgSize[MAX_PLUGIN_COUNT]; 38 | u32 arm11BinStart; 39 | u32 arm11BinSize; 40 | u32 tid[2]; 41 | u32 gamePluginPid; 42 | u32 gamePluginMenuAddr; 43 | u32 currentLanguage; 44 | u32 nightShiftLevel; 45 | 46 | // Plugin's PLGLOADER_INFO ends here 47 | } PLGLOADER_INFO; 48 | 49 | #define MAX_GAME_PLUGIN_MENU_ENTRY 64 50 | #define GAME_PLUGIN_MENU_BUF_SIZE 3000 51 | typedef struct { 52 | u8 state[MAX_GAME_PLUGIN_MENU_ENTRY]; 53 | u16 offsetInBuffer[MAX_GAME_PLUGIN_MENU_ENTRY]; 54 | u16 bufOffset, count; 55 | u8 buf[GAME_PLUGIN_MENU_BUF_SIZE]; 56 | } GAME_PLUGIN_MENU; 57 | 58 | static PLGLOADER_INFO *const plgLoader = (PLGLOADER_INFO *)PLG_POOL_ADDR; 59 | static PLGLOADER_EX_INFO *const plgLoaderEx = &ntrConfig->ex.plg; 60 | 61 | extern int plgOverlayStatus; 62 | extern int plgHasVRAMAccess; 63 | extern int plgHasOverlay; 64 | 65 | void handlePortCmd(u32 cmd_id, u32 norm_param_count, u32 trans_param_size, u32 *cmd_buf1); 66 | void handlePortThreadPre(void); 67 | void handlePortThread(void *arg); 68 | Handle rpGetPortHandle(void); 69 | void rpSetGamePid(u32 gamePid); 70 | int remotePlayMenu(u32 localaddr); 71 | int nsDbgNext(void); 72 | u32 plgRegisterCallback(u32 type, void* callback, u32); 73 | u32 plgSetValue(u32 index, u32 value); 74 | u32 plgGetIoBase(u32 IoBase); 75 | void plgInitScreenOverlay(u32 *stack); 76 | void plgInitScreenOverlayDirectly(u32 funcAddr); 77 | void plgSetBufferSwapHandle(u32 isDisplay1, u32 addr, u32 addrB, u32 stride, u32 format, u32 flushAlways); 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /include/ns.h: -------------------------------------------------------------------------------- 1 | #ifndef NS_H 2 | #define NS_H 3 | 4 | #include "3ds/types.h" 5 | #include "ntr_config.h" 6 | #include "rp.h" 7 | #include "rt.h" 8 | #include "constants.h" 9 | 10 | #include 11 | 12 | void nsDbgPrint2(const char *title, const char *msg); 13 | void nsDbgPrintVerboseVABuf(const char *file_name, int line_number, const char *func_name, const char* fmt, va_list arp); 14 | void nsDbgPrintVerboseVA(const char *file_name, int line_number, const char *func_name, const char* fmt, va_list arp); 15 | void nsDbgPrintRaw(const char *fmt, ...) __attribute__((format(printf, 1, 2))); 16 | #define nsDbgPrint(fmt, ...) nsDbgPrintVerbose(__FILE__, __LINE__, __func__, fmt, ## __VA_ARGS__) 17 | void nsDbgPrintVerbose(const char *file_name, int line_number, const char *func_name, const char* fmt, ...) __attribute__((format(printf, 4, 5))); 18 | 19 | typedef enum { 20 | NS_INITMODE_FROMBOOT, 21 | NS_INITMODE_FROMHOOK = 2, 22 | } NS_INITMODE; 23 | 24 | typedef struct { 25 | u32 kcp_mode; 26 | u32 kcp_qos; 27 | struct overlay_stats_screen_t { 28 | s32 comp_size; 29 | u32 frame_time; 30 | struct { 31 | struct { 32 | s32 m; 33 | s32 p; 34 | s32 d; 35 | } f[RP_DELTA_Q_COEFS_COUNT]; 36 | s32 qb; 37 | s32 qc; 38 | s32 nbits; 39 | u32 qd; 40 | } delta_q; 41 | } s[RP_SCREEN_COUNT]; 42 | } OVERLAY_STATS_INFO; 43 | 44 | typedef struct { 45 | NS_INITMODE initMode; 46 | u32 startupCommand_unused; 47 | u32 hSOCU_unused; 48 | 49 | u8 *debugBuf_unused; 50 | u8 *debugBufEnd_unused; 51 | u8 *debugPtr; 52 | u32 debugReady; 53 | 54 | RT_LOCK debugBufferLock; 55 | 56 | u32 startupInfo[32]; 57 | u32 hasDirectScreenAccess_unused; 58 | u32 exitFlag_unused; 59 | 60 | u32 sharedFunc[100]; 61 | 62 | /* Plugin's NS_CONFIG ends here */ 63 | NTR_CONFIG ntrConfig; 64 | OVERLAY_STATS_INFO ovStats; 65 | RP_CONFIG rpConfig; 66 | } NS_CONFIG; 67 | 68 | static NS_CONFIG *const nsConfig = (NS_CONFIG *)NS_CONFIG_ADDR; 69 | static RP_CONFIG *const rpConfig = &nsConfig->rpConfig; 70 | 71 | void nsThreadInit(); 72 | int nsStartup(void); 73 | int nsCheckPCSafeToWrite(u32 hProcess, u32 remotePC); 74 | u32 nsAttachProcess(Handle hProcess, u32 remotePC, NS_CONFIG *cfg, int thumbR3); 75 | void nsHandlePacket(void); 76 | void nsHandleDbgPrintPacket(void); 77 | void nsHandleMenuPacket(void); 78 | int nsControlRecv(int); 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /include/ntr_config.h: -------------------------------------------------------------------------------- 1 | #ifndef NTR_CONFIG_H 2 | #define NTR_CONFIG_H 3 | 4 | #include "3ds/types.h" 5 | #include "constants.h" 6 | 7 | typedef struct { 8 | u32 noPlugins; 9 | u32 CTRPFCompat; 10 | u32 remotePlayBoost; 11 | u32 overlayStats; 12 | u32 noLoaderMem; 13 | u32 memSizeTotal; 14 | } PLGLOADER_EX_INFO; 15 | 16 | typedef struct { 17 | u32 nsUseDbg; 18 | PLGLOADER_EX_INFO plg; 19 | } NTR_EX_CONFIG; 20 | 21 | typedef struct { 22 | u32 bootNTRVersion; 23 | u32 isNew3DS; 24 | u32 firmVersion; 25 | 26 | u32 IoBasePad; 27 | u32 IoBaseLcd; 28 | u32 IoBasePdc; 29 | u32 PMSvcRunAddr; 30 | u32 PMPid; 31 | u32 HomeMenuPid; 32 | 33 | u32 HomeMenuVersion; 34 | u32 HomeMenuInjectAddr; // FlushDataCache function 35 | u32 HomeFSReadAddr; 36 | u32 HomeFSUHandleAddr; 37 | u32 HomeCardUpdateInitAddr; 38 | u32 HomeAptStartAppletAddr; 39 | 40 | u32 KProcessHandleDataOffset; 41 | u32 KProcessPIDOffset; 42 | u32 KProcessCodesetOffset; 43 | u32 ControlMemoryPatchAddr1; 44 | u32 ControlMemoryPatchAddr2; 45 | u32 KernelFreeSpaceAddr_Optional; 46 | u32 KMMUHaxAddr; 47 | u32 KMMUHaxSize; 48 | u32 InterProcessDmaFinishState; 49 | u32 fsUserHandle; 50 | u32 arm11BinStart; 51 | u32 arm11BinSize; 52 | u32 showDbgFunc; 53 | 54 | u32 memMode; 55 | char ntrFilePath[0x100]; 56 | 57 | /* BootNTR's NTR_CONFIG ends here */ 58 | NTR_EX_CONFIG ex; 59 | } NTR_CONFIG; 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /include/proc.h: -------------------------------------------------------------------------------- 1 | #ifndef PROC_H 2 | #define PROC_H 3 | 4 | #include "3ds/types.h" 5 | 6 | u32 getCurrentProcessId(void); 7 | u32 getCurrentProcessHandle(void); 8 | u32 getProcessTIDByHandle(u32 hProcess, u32 tid[]); 9 | 10 | u32 mapRemoteMemory(Handle hProcess, u32 addr, u32 size, u32 op); 11 | u32 mapRemoteMemoryInLoader(Handle hProcess, u32 addr, u32 size, u32 op); 12 | u32 protectRemoteMemory(Handle hProcess, void* addr, u32 size, u32 perm); 13 | u32 protectMemory(void *addr, u32 size, u32 perm); 14 | u32 copyRemoteMemoryTimeout(Handle hDst, void *ptrDst, Handle hSrc, void *ptrSrc, u32 size, s64 timeout); 15 | u32 copyRemoteMemory(Handle hDst, void* ptrDst, Handle hSrc, void* ptrSrc, u32 size); 16 | 17 | void showDbgMemInfo(Handle hProcess, u32 addr); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /include/rp.h: -------------------------------------------------------------------------------- 1 | #ifndef RP_H 2 | #define RP_H 3 | 4 | #include "3ds/types.h" 5 | 6 | typedef struct { 7 | u32 mode; 8 | u32 quality; 9 | u32 qos; // in bytes per second 10 | u32 coreCount; 11 | u32 dstPort; 12 | u32 dstAddr; 13 | u32 threadPriority; 14 | u32 gamePid; 15 | u32 chromaSs; 16 | } RP_CONFIG; 17 | 18 | int rpStartupFromMenu(RP_CONFIG *config); 19 | void rpStartup(u8 *buf); 20 | 21 | typedef u32 (*sendPacketTypedef)(u8 *, u32); 22 | extern sendPacketTypedef nwmSendPacket; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /include/rt.h: -------------------------------------------------------------------------------- 1 | #ifndef RT_H 2 | #define RT_H 3 | 4 | #include "3ds/types.h" 5 | #include "3ds/svc.h" 6 | #include "3ds/synchronization.h" 7 | 8 | typedef struct _RT_HOOK { 9 | u32 model; 10 | u32 isEnabled; 11 | u32 funcAddr; 12 | u32 bakCode[16]; 13 | u32 jmpCode[16]; 14 | u32 callCode[16]; 15 | } RT_HOOK; 16 | 17 | typedef LightLock RT_LOCK; 18 | _Static_assert(sizeof(RT_LOCK) == sizeof(u32)); 19 | void rtInitLock(RT_LOCK *lock); 20 | void rtAcquireLock(RT_LOCK *lock); 21 | void rtReleaseLock(RT_LOCK *lock); 22 | 23 | void rtGenerateJumpCodeThumbR3(u32 dst, u32 *buf); 24 | void rtGenerateJumpCode(u32 dst, u32 *buf); 25 | void rtInitHookThumb(RT_HOOK *hook, u32 funcAddr, u32 callbackAddr); 26 | void rtInitHook(RT_HOOK *hook, u32 funcAddr, u32 callbackAddr); 27 | void rtEnableHook(RT_HOOK *hook); 28 | void rtDisableHook(RT_HOOK *hook); 29 | 30 | u32 rtAlignToPageSize(u32 size); 31 | u32 rtGetPageOfAddress(u32 addr); 32 | u32 rtCheckRemoteMemory(Handle hProcess, u32 addr, u32 size, MemPerm perm); 33 | u32 rtCheckMemory(u32 addr, u32 size, MemPerm perm); 34 | u32 rtGetThreadReg(Handle hProcess, u32 tid, u32 *ctx); 35 | u32 rtFlushInstructionCache(void *ptr, u32 size); 36 | 37 | int rtRecvSocket(u32 sockfd, u8 *buf, int size); 38 | int rtSendSocket(u32 sockfd, u8 *buf, int size); 39 | 40 | Handle rtOpenFile(char *fileName); 41 | Handle rtOpenFile16(u16 *fileName); 42 | u32 rtGetFileSize(Handle file); 43 | u32 rtLoadFileToBuffer(Handle file, void *pBuf, u32 bufSize); 44 | void rtCloseFile(Handle file); 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /include/svc7b.h: -------------------------------------------------------------------------------- 1 | #ifndef SVC7B_H 2 | #define SVC7B_H 3 | 4 | #include "3ds/types.h" 5 | 6 | extern u32 KProcessHandleDataOffset; 7 | extern u32 KProcessPIDOffset; 8 | extern u32 KProcessCodesetOffset; 9 | 10 | void kmemcpy(void *dst, void *src, u32 size); 11 | u32 kGetKProcessByHandle(u32 handle); 12 | u32 kGetCurrentKProcess(void); 13 | void kSetCurrentKProcess(u32 ptr); 14 | u32 kSwapProcessPid(u32 kProcess, u32 newPid); 15 | void kDoKernelHax(NTR_CONFIG *ntrCfg); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /include/ui.h: -------------------------------------------------------------------------------- 1 | #ifndef UI_H 2 | #define UI_H 3 | 4 | #include "ns.h" 5 | 6 | void showMsgRaw2(const char *title, const char *msg); 7 | int showMsgVAPre(void); 8 | int showMsgVA(const char *file_name, int line_number, const char *func_name, const char* fmt, va_list va); 9 | int showMsgRaw(const char *fmt, ...) __attribute__((format(printf, 1, 2))); 10 | #define showMsg(fmt, ...) showMsgVerbose(__FILE__, __LINE__, __func__, fmt, ## __VA_ARGS__) 11 | int showMsgVerbose(const char *file_name, int line_number, const char *func_name, const char *fmt, ...) __attribute__((format(printf, 4, 5))); 12 | void printTitleAndMsg(char title[LOCAL_TITLE_BUF_SIZE], const char *file_name, int line_number, const char *func_name, char msg[LOCAL_MSG_BUF_SIZE], const char* fmt, va_list va); 13 | 14 | #define showDbg(fmt, ...) do { \ 15 | if (!showDbgFunc) \ 16 | nsDbgPrint(fmt, ## __VA_ARGS__); \ 17 | showMsg(fmt, ## __VA_ARGS__); \ 18 | } while (0) 19 | 20 | #define showDbgRaw(fmt, ...) do { \ 21 | if (!showDbgFunc) \ 22 | nsDbgPrintRaw(fmt, ## __VA_ARGS__); \ 23 | showMsgRaw(fmt, ## __VA_ARGS__); \ 24 | } while (0) 25 | 26 | void panicHandle(const char *file, int file_len, int line, int column); 27 | 28 | void disp(u32 t, u32 cl); 29 | 30 | extern u32 hasDirectScreenAccess; 31 | int initDirectScreenAccess(void); 32 | void acquireVideo(void); 33 | void releaseVideo(void); 34 | void updateScreen(void); 35 | 36 | const char *plgTranslate(const char *msg); 37 | 38 | Handle menuGetPortHandle(void); 39 | s32 showMenu(const char *title, u32 entriesCount, const char *captions[]); 40 | s32 showMenuEx(const char *title, u32 entriesCount, const char *captions[], const char *descriptions[], u32 selectOn); 41 | s32 showMenuEx2(const char *title, u32 entriesCount, const char *captions[], const char *descriptions[], u32 selectOn, u32 *keysPressed); 42 | 43 | void blank(void); 44 | int print(const char *s, int x, int y, u8 r, u8 g, u8 b); 45 | 46 | extern u32 waitKeysOverride; 47 | u32 waitKeys(void); 48 | void debounceKeys(void); 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /include/xprintf.h: -------------------------------------------------------------------------------- 1 | #ifndef XPRINTF_H 2 | #define XPRINTF_H 3 | 4 | #include 5 | #include 6 | size_t xsprintf(char *buf, const char *fmt, ...) __attribute__((format(printf, 2, 3))); 7 | size_t xsnprintf(char *buf, size_t bufLen, const char *fmt, ...) __attribute__((format(printf, 3, 4))); 8 | size_t xvsnprintf(char *buf, size_t bufLen, const char *fmt, va_list va); 9 | 10 | /** Holds a user defined output stream */ 11 | struct ostrm 12 | { 13 | /** Parameter to pass the the function. */ 14 | void *p; 15 | /** Send a memory block ta a user defined stream. 16 | * @param p Parameter. 17 | * @param src Source memory block. 18 | * @param len Length in bytes of the memory block. */ 19 | void (*func)(void *p, void const *src, size_t len); 20 | }; 21 | 22 | size_t xprintf(struct ostrm const* o, char const* fmt, ... ) __attribute__((format(printf, 2, 3))); 23 | size_t xvprintf(struct ostrm const* o, char const* fmt, va_list va); 24 | 25 | int strnjoin(char *dst, size_t dst_len, const char *s1, const char *s2); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /make.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | make -j$(nproc) -C libctru/libctru lib/libctru.a 4 | make target/armv6k-nintendo-3ds/release/libnwm_rs.a 5 | make -j$(nproc) "$@" 6 | -------------------------------------------------------------------------------- /prepare.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | git submodule update --init --recursive 3 | -------------------------------------------------------------------------------- /source/boot/main_boot.c: -------------------------------------------------------------------------------- 1 | #include "global.h" 2 | 3 | #include "3ds/services/fs.h" 4 | 5 | #include 6 | #include 7 | 8 | extern volatile int _BootArgs[]; 9 | static NTR_CONFIG *ntrCfg; 10 | static Handle fsUserHandle; 11 | 12 | static void initBootVars(void) { 13 | ntrCfg = (void *)_BootArgs[0]; 14 | showDbgFunc = (void *)ntrCfg->showDbgFunc; 15 | fsUserHandle = ntrCfg->fsUserHandle; 16 | arm11BinStart = ntrCfg->arm11BinStart; 17 | arm11BinSize = ntrCfg->arm11BinSize; 18 | loadParams(ntrCfg); 19 | } 20 | 21 | static void doKernelHax(void) { 22 | showMsgRaw("Doing kernel hax..."); 23 | kDoKernelHax(ntrCfg); 24 | showMsgRaw("Kernel hax done."); 25 | 26 | disp(100, DBG_CL_INFO); 27 | } 28 | 29 | static void dbgDumpCode(u32 base, u32 size, char *fileName) { 30 | u32 off = 0; 31 | u8 tmpBuffer[0x1000]; 32 | Handle handle; 33 | u32 t; 34 | 35 | fsUseSession(fsUserHandle); 36 | Result res; 37 | res = FSUSER_OpenFileDirectly(&handle, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, NULL), fsMakePath(PATH_ASCII, fileName), FS_OPEN_WRITE | FS_OPEN_CREATE, 0); 38 | if (res != 0) { 39 | showMsgRaw("Saving dump failed"); 40 | return; 41 | } 42 | 43 | while(off < size) { 44 | kmemcpy(tmpBuffer, (u8 *)base + off, 0x1000); 45 | FSFILE_Write(handle, &t, off, tmpBuffer, 0x1000, 0); 46 | off += 0x1000; 47 | } 48 | FSFILE_Close(handle); 49 | } 50 | 51 | static int injectToHomeMenu(void) { 52 | NS_CONFIG cfg = { 0 }; 53 | Handle hProcess = 0; 54 | s32 ret = 0; 55 | ret = svcOpenProcess(&hProcess, ntrCfg->HomeMenuPid); 56 | if (ret != 0) { 57 | showDbgRaw("Failed to open home menu process: %08"PRIx32, ret); 58 | goto final; 59 | } 60 | 61 | memcpy(&cfg.ntrConfig, ntrCfg, offsetof(NTR_CONFIG, ex)); 62 | cfg.ntrConfig.ex.nsUseDbg = nsDbgNext(); 63 | if (cfg.ntrConfig.ex.nsUseDbg) { 64 | dbgDumpCode(0xdff80000, 0x80000, "/axiwram.dmp"); 65 | disp(100, DBG_CL_USE_DBG); 66 | } 67 | 68 | ret = nsAttachProcess(hProcess, ntrCfg->HomeMenuInjectAddr, &cfg, 0); 69 | 70 | svcCloseHandle(hProcess); 71 | 72 | if (ret != 0) { 73 | showDbgRaw("Attach to home menu process failed: %08"PRIx32, ret); 74 | goto final; 75 | } 76 | 77 | final: 78 | return ret; 79 | } 80 | 81 | int main(void) { 82 | initBootVars(); 83 | 84 | doKernelHax(); 85 | 86 | if (injectToHomeMenu() != 0) 87 | disp(100, DBG_CL_FATAL); 88 | 89 | return 0; 90 | } 91 | 92 | int showMsgVA(const char *, int , const char *, const char *fmt, va_list va) { 93 | size_t fmt_len = strlen(fmt); 94 | char buf[fmt_len + 1]; 95 | memcpy(buf, fmt, sizeof(buf)); 96 | size_t buf_len = fmt_len; 97 | while (buf_len && buf[--buf_len] == '\n') { 98 | buf[buf_len] = 0; 99 | } 100 | 101 | char msg[LOCAL_MSG_BUF_SIZE]; 102 | xvsnprintf(msg, LOCAL_MSG_BUF_SIZE, buf, va); 103 | return showMsgDbgFunc(msg); 104 | } 105 | 106 | void nsDbgPrintRaw(const char *fmt, ...) { 107 | va_list arp; 108 | va_start(arp, fmt); 109 | showMsgVA(NULL, 0, NULL, fmt, arp); 110 | va_end(arp); 111 | } 112 | 113 | void nsDbgPrintVerbose(const char *, int, const char *, const char* fmt, ...) { 114 | va_list arp; 115 | va_start(arp, fmt); 116 | showMsgVA(NULL, 0, NULL, fmt, arp); 117 | va_end(arp); 118 | } 119 | 120 | int nsCheckPCSafeToWrite(u32 hProcess, u32 remotePC) { 121 | s32 ret, i; 122 | u32 tids[LOCAL_TID_BUF_COUNT]; 123 | s32 tidCount; 124 | u32 ctx[400]; 125 | 126 | ret = svcGetThreadList(&tidCount, tids, LOCAL_TID_BUF_COUNT, hProcess); 127 | if (ret != 0) { 128 | return -1; 129 | } 130 | 131 | for (i = 0; i < tidCount; ++i) { 132 | u32 tid = tids[i]; 133 | memset(ctx, 0x33, sizeof(ctx)); 134 | if (rtGetThreadReg(hProcess, tid, ctx) != 0) 135 | return -1; 136 | u32 pc = ctx[15]; 137 | if (remotePC >= pc - 24 && remotePC < pc + 8) 138 | return -1; 139 | } 140 | 141 | return 0; 142 | } 143 | -------------------------------------------------------------------------------- /source/bootloader.s: -------------------------------------------------------------------------------- 1 | .arm 2 | .align(4); 3 | .section .text 4 | .global _Reset 5 | _Reset: 6 | b _Start 7 | .global _BootArgs 8 | _BootArgs: 9 | nop 10 | nop 11 | nop 12 | nop 13 | nop 14 | nop 15 | nop 16 | nop 17 | _Start: 18 | STMFD SP!, {R0-R12, LR}; 19 | MRS R0, CPSR 20 | STMFD SP!, {R0} 21 | 22 | LDR R6, =_Reset 23 | ADR R5, _Reset 24 | sub r5, r5, r6 /* r5 = realAddress - baseAddress */ 25 | ldr r6, =__rel_dyn_start 26 | ldr r7, =__rel_dyn_end 27 | add r6, r6, r5 28 | add r7, r7, r5 29 | relocNotFinished: 30 | ldmia r6!, {r3, r4} 31 | cmp r4, #0x17 32 | bne notRelativeEntry 33 | add r3, r3, r5 34 | ldr r4, [r3] 35 | add r4, r4, r5 36 | str r4, [r3] 37 | notRelativeEntry: 38 | cmp r6, r7 39 | bcc relocNotFinished 40 | ldr r0, =0xffff8001 41 | adr r1, _Reset 42 | ldr r2, =__bss_start 43 | sub r2, r2, r1 /* r2 = codesize */ 44 | svc 0x54 /* flush instruction cache */ 45 | nop 46 | nop 47 | 48 | mov r0, sp 49 | bl c_entry 50 | 51 | ldmfd sp!, {r0} 52 | msr cpsr, r0 53 | ldmfd SP!, {R0-R12, LR}; 54 | 55 | .global _ReturnToUser 56 | _ReturnToUser: 57 | bx lr 58 | nop 59 | nop 60 | nop 61 | msr cpsr, r0 62 | /* unused 63 | ldr PC, =c_entry 64 | */ 65 | 66 | .global __rel_dyn_start 67 | __rel_dyn_start: 68 | 69 | .global __rel_dyn_end 70 | __rel_dyn_end: 71 | 72 | .section .__bss_start 73 | __bss_start: 74 | 75 | .section .__bss_end 76 | __bss_end: 77 | -------------------------------------------------------------------------------- /source/csvc.s: -------------------------------------------------------------------------------- 1 | @ This paricular file is licensed under the following terms: 2 | 3 | @ This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable 4 | @ for any damages arising from the use of this software. 5 | @ 6 | @ Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it 7 | @ and redistribute it freely, subject to the following restrictions: 8 | @ 9 | @ The origin of this software must not be misrepresented; you must not claim that you wrote the original software. 10 | @ If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 11 | @ 12 | @ Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 13 | @ This notice may not be removed or altered from any source distribution. 14 | 15 | .arm 16 | .balign 4 17 | 18 | .macro SVC_BEGIN name 19 | .section .text.\name, "ax", %progbits 20 | .global \name 21 | .type \name, %function 22 | .align 2 23 | .cfi_startproc 24 | \name: 25 | .endm 26 | 27 | .macro SVC_END 28 | .cfi_endproc 29 | .endm 30 | 31 | SVC_BEGIN svcCustomBackdoor 32 | svc 0x80 33 | bx lr 34 | SVC_END 35 | 36 | SVC_BEGIN svcConvertVAToPA 37 | svc 0x90 38 | bx lr 39 | SVC_END 40 | 41 | SVC_BEGIN svcFlushDataCacheRange 42 | svc 0x91 43 | bx lr 44 | SVC_END 45 | 46 | SVC_BEGIN svcFlushEntireDataCache 47 | svc 0x92 48 | bx lr 49 | SVC_END 50 | 51 | SVC_BEGIN svcInvalidateInstructionCacheRange 52 | svc 0x93 53 | bx lr 54 | SVC_END 55 | 56 | SVC_BEGIN svcInvalidateEntireInstructionCache 57 | svc 0x94 58 | bx lr 59 | SVC_END 60 | 61 | SVC_BEGIN svcMapProcessMemoryEx 62 | str r4, [sp, #-4]! 63 | ldr r4, [sp, #4] 64 | svc 0xA0 65 | ldr r4, [sp], #4 66 | bx lr 67 | SVC_END 68 | 69 | SVC_BEGIN svcUnmapProcessMemoryEx 70 | svc 0xA1 71 | bx lr 72 | SVC_END 73 | 74 | SVC_BEGIN svcControlMemoryEx 75 | push {r0, r4, r5} 76 | ldr r0, [sp, #0xC] 77 | ldr r4, [sp, #0xC+0x4] 78 | ldr r5, [sp, #0xC+0x8] 79 | svc 0xA2 80 | pop {r2, r4, r5} 81 | str r1, [r2] 82 | bx lr 83 | SVC_END 84 | 85 | SVC_BEGIN svcControlMemoryUnsafe 86 | str r4, [sp, #-4]! 87 | ldr r4, [sp, #4] 88 | svc 0xA3 89 | ldr r4, [sp], #4 90 | bx lr 91 | SVC_END 92 | 93 | SVC_BEGIN svcFreeMemory 94 | svc 0xA3 95 | bx lr 96 | SVC_END 97 | 98 | SVC_BEGIN svcControlService 99 | svc 0xB0 100 | bx lr 101 | SVC_END 102 | 103 | SVC_BEGIN svcCopyHandle 104 | str r0, [sp, #-4]! 105 | svc 0xB1 106 | ldr r2, [sp], #4 107 | str r1, [r2] 108 | bx lr 109 | SVC_END 110 | 111 | SVC_BEGIN svcTranslateHandle 112 | str r0, [sp, #-4]! 113 | svc 0xB2 114 | ldr r2, [sp], #4 115 | str r1, [r2] 116 | bx lr 117 | SVC_END 118 | 119 | SVC_BEGIN svcControlProcess 120 | svc 0xB3 121 | bx lr 122 | SVC_END 123 | -------------------------------------------------------------------------------- /source/entry.c: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | void c_entry(u32 *) { 4 | main(); 5 | } 6 | -------------------------------------------------------------------------------- /source/game/main_game.c: -------------------------------------------------------------------------------- 1 | #include "global.h" 2 | 3 | #include "3ds/srv.h" 4 | #include "3ds/ipc.h" 5 | #include "3ds/services/gspgpu.h" 6 | 7 | #include 8 | 9 | typedef enum { 10 | CALLBACK_TYPE_OVERLAY = 101, 11 | } CALLBACK_TYPE; 12 | 13 | #define MAX_PLUGIN_ENTRY 64 14 | typedef struct { 15 | CALLBACK_TYPE type; 16 | char *title; 17 | void *callback; 18 | } PLUGIN_ENTRY; 19 | static PLUGIN_ENTRY plgEntries[MAX_PLUGIN_ENTRY]; 20 | static u32 plgEntriesCount; 21 | 22 | typedef int (*drawStringTypeDef)(char *str, int x, int y, u8 r, u8 g, u8 b, int newLine); 23 | typedef char *(*translateTypeDef)(char *str); 24 | 25 | static translateTypeDef plgTranslateCallback; 26 | static drawStringTypeDef plgDrawStringCallback; 27 | 28 | typedef u32 (*OverlayFnTypedef)(u32 isDisplay1, u32 addr, u32 addrB, u32 width, u32 format); 29 | void plgSetBufferSwapHandle(u32 isDisplay1, u32 addr, u32 addrB, u32 stride, u32 format, u32 flushAlways) { 30 | s32 ret; 31 | 32 | if (!plgHasOverlay) 33 | return; 34 | 35 | if ((addr >= 0x1f000000) && (addr < 0x1f600000)) { 36 | if (!plgHasVRAMAccess) { 37 | return; 38 | } 39 | } 40 | 41 | u32 height = isDisplay1 ? GSP_SCREEN_HEIGHT_BOTTOM : GSP_SCREEN_HEIGHT_TOP; 42 | int isDirty = flushAlways; 43 | 44 | if (!isDirty) { 45 | svcInvalidateProcessDataCache(CUR_PROCESS_HANDLE, (u32)addr, stride * height); 46 | if ((isDisplay1 == 0) && (addrB) && (addrB != addr)) { 47 | svcInvalidateProcessDataCache(CUR_PROCESS_HANDLE, (u32)addrB, stride * height); 48 | } 49 | } 50 | 51 | for (u32 i = 0; i < plgEntriesCount; ++i) { 52 | if (plgEntries[i].type == CALLBACK_TYPE_OVERLAY) { 53 | ret = ((OverlayFnTypedef)plgEntries[i].callback)(isDisplay1, addr, addrB, stride, format); 54 | if (ret == 0) { 55 | isDirty = 1; 56 | } 57 | } 58 | } 59 | 60 | if (isDirty) { 61 | svcFlushProcessDataCache(CUR_PROCESS_HANDLE, (u32)addr, stride * height); 62 | if ((isDisplay1 == 0) && (addrB) && (addrB != addr)) { 63 | svcFlushProcessDataCache(CUR_PROCESS_HANDLE, (u32)addrB, stride * height); 64 | } 65 | } 66 | } 67 | 68 | void mainPost(void) { 69 | if (plgLoaderEx->remotePlayBoost || plgLoaderEx->overlayStats) 70 | plgInitScreenOverlay(NULL); 71 | 72 | if (!plgLoaderEx->noPlugins && plgLoaderEx->memSizeTotal != 0) { 73 | disp(100, DBG_CL_USE_INJECT); 74 | 75 | initSharedFunc(); 76 | for (u32 i = 0; i < plgLoader->plgCount; ++i) { 77 | typedef void (*funcType)(void); 78 | ((funcType)plgLoader->plgBufferPtr[i])(); 79 | } 80 | } 81 | } 82 | 83 | void __system_initSyscalls(void); 84 | Result __sync_init(void); 85 | void mainThread(void *) { 86 | __system_initSyscalls(); 87 | s32 ret; 88 | ret = __sync_init(); 89 | if (ret != 0) { 90 | nsDbgPrint("sync init failed: %08"PRIx32"\n", ret); 91 | goto final; 92 | } 93 | 94 | ret = srvInit(); 95 | if (ret != 0) { 96 | showDbg("srvInit failed: %08"PRIx32, ret); 97 | goto final; 98 | } 99 | 100 | if (ntrConfig->ex.nsUseDbg) { 101 | ret = nsStartup(); 102 | if (ret != 0) { 103 | disp(100, DBG_CL_USE_DBG_FAIL); 104 | } else { 105 | disp(100, DBG_CL_USE_DBG); 106 | } 107 | } 108 | 109 | final: 110 | svcExitThread(); 111 | } 112 | 113 | u32 plgRegisterCallback(u32 type, void *callback, u32) { 114 | if (type == CALLBACK_TYPE_OVERLAY) { 115 | plgInitScreenOverlay(NULL); 116 | 117 | if (plgOverlayStatus != 1) { 118 | return -1; 119 | } 120 | 121 | if (plgEntriesCount >= MAX_PLUGIN_ENTRY) { 122 | return -1; 123 | } 124 | 125 | plgEntries[plgEntriesCount++] = (PLUGIN_ENTRY){ 126 | .type = CALLBACK_TYPE_OVERLAY, 127 | .title = "ov", 128 | .callback = callback 129 | }; 130 | 131 | plgHasOverlay = 1; 132 | 133 | return 0; 134 | } 135 | 136 | return -1; 137 | } 138 | 139 | enum { 140 | IO_BASE_PAD = 1, 141 | IO_BASE_LCD, 142 | IO_BASE_PDC, 143 | IO_BASE_GSPHEAP, 144 | IO_BASE_HOME_MENU_PID 145 | }; 146 | 147 | enum { 148 | VALUE_CURRENT_LANGUAGE = 6, 149 | VALUE_DRAWSTRING_CALLBACK, 150 | VALUE_TRANSLATE_CALLBACK 151 | }; 152 | 153 | u32 plgSetValue(u32 index, u32 value) { 154 | switch (index) { 155 | case VALUE_CURRENT_LANGUAGE: 156 | plgLoader->currentLanguage = value; 157 | break; 158 | 159 | case VALUE_DRAWSTRING_CALLBACK: 160 | plgDrawStringCallback = (void*)value; 161 | break; 162 | 163 | case VALUE_TRANSLATE_CALLBACK: 164 | plgTranslateCallback = (void*)value; 165 | break; 166 | 167 | default: 168 | break; 169 | } 170 | return 0; 171 | } 172 | 173 | u32 plgGetIoBase(u32 IoBase) { 174 | switch (IoBase) { 175 | case IO_BASE_LCD: 176 | return IoBaseLcd; 177 | 178 | case IO_BASE_PAD: 179 | return IoBasePad; 180 | 181 | case IO_BASE_PDC: 182 | return IoBasePdc; 183 | 184 | case IO_BASE_GSPHEAP: 185 | return 0x14000000; 186 | 187 | case IO_BASE_HOME_MENU_PID: 188 | return ntrConfig->HomeMenuPid; 189 | 190 | default: 191 | return plgLoader->currentLanguage; 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /source/misc.s: -------------------------------------------------------------------------------- 1 | .arm 2 | .align(4); 3 | 4 | .type __kmemcpyHandler, %function 5 | __kmemcpyHandler: 6 | cpsid aif 7 | ldr r0, [sp, #8] 8 | ldr r1, [sp, #12] 9 | ldr r2, [sp, #16] 10 | b kememcpy 11 | 12 | .global kmemcpy 13 | .type kmemcpy, %function 14 | kmemcpy: 15 | push {r0, r1, r2, r3} 16 | ldr r0, =__kmemcpyHandler 17 | svc 0x7B 18 | add sp, #16 19 | bx lr 20 | 21 | .type __kGetKProcessByHandleHandler, %function 22 | __kGetKProcessByHandleHandler: 23 | cpsid aif 24 | ldr r0, [sp, #8] 25 | push {r2, lr} 26 | bl keGetKProcessByHandle 27 | pop {r2, lr} 28 | str r0, [sp, #8] 29 | bx lr 30 | 31 | .global kGetKProcessByHandle 32 | .type kGetKProcessByHandle, %function 33 | kGetKProcessByHandle: 34 | push {r0, r1} 35 | ldr r0, =__kGetKProcessByHandleHandler 36 | svc 0x7B 37 | pop {r0, r1} 38 | bx lr 39 | 40 | .type __kGetCurrentKProcessHandler, %function 41 | __kGetCurrentKProcessHandler: 42 | cpsid aif 43 | push {r2, lr} 44 | bl keGetCurrentKProcess 45 | pop {r2, lr} 46 | str r0, [sp, #8] 47 | bx lr 48 | 49 | .global kGetCurrentKProcess 50 | .type kGetCurrentKProcess, %function 51 | kGetCurrentKProcess: 52 | sub sp, #8 53 | ldr r0, =__kGetCurrentKProcessHandler 54 | svc 0x7B 55 | pop {r0, r1} 56 | bx lr 57 | 58 | .type __kSetCurrentKProcessHandler, %function 59 | __kSetCurrentKProcessHandler: 60 | cpsid aif 61 | ldr r0, [sp, #8] 62 | b keSetCurrentKProcess 63 | 64 | .global kSetCurrentKProcess 65 | .type kSetCurrentKProcess, %function 66 | kSetCurrentKProcess: 67 | push {r0, r1} 68 | ldr r0, =__kSetCurrentKProcessHandler 69 | svc 0x7B 70 | add sp, #8 71 | bx lr 72 | 73 | .type __kSwapProcessPidHandler, %function 74 | __kSwapProcessPidHandler: 75 | cpsid aif 76 | ldr r0, [sp, #8] 77 | ldr r1, [sp, #12] 78 | push {r2, lr} 79 | bl keSwapProcessPid 80 | pop {r2, lr} 81 | str r0, [sp, #8] 82 | bx lr 83 | 84 | .global kSwapProcessPid 85 | .type kSwapProcessPid, %function 86 | kSwapProcessPid: 87 | push {r0, r1} 88 | ldr r0, =__kSwapProcessPidHandler 89 | svc 0x7B 90 | pop {r0, r1} 91 | bx lr 92 | 93 | .type __kDoKernelHaxHandler, %function 94 | __kDoKernelHaxHandler: 95 | cpsid aif 96 | ldr r0, [sp, #8] 97 | b keDoKernelHax 98 | 99 | .global kDoKernelHax 100 | .type kDoKernelHax, %function 101 | kDoKernelHax: 102 | push {r0, r1} 103 | ldr r0, =__kDoKernelHaxHandler 104 | svc 0x7B 105 | add sp, #8 106 | bx lr 107 | 108 | .global InvalidateEntireInstructionCache 109 | .type InvalidateEntireInstructionCache, %function 110 | InvalidateEntireInstructionCache: 111 | mov r0, #0 112 | mcr p15, 0, r0, c7, c5, 0 113 | bx lr 114 | 115 | .global InvalidateEntireDataCache 116 | .type InvalidateEntireDataCache, %function 117 | InvalidateEntireDataCache: 118 | mov r0, #0 119 | mcr p15, 0, r0, c7, c14, 0 @Clean and Invalidate Entire Data Cache 120 | mcr p15, 0, r0, c7, c10, 0 121 | mcr p15, 0, R0,c7,c10, 4 @Data Synchronization Barrier 122 | mcr p15, 0, R0,c7,c5, 4 @Flush Prefetch Buffer 123 | bx lr 124 | 125 | .type __ctr_memcpy_cache, %function 126 | __ctr_memcpy_cache: 127 | STMFD SP!, {R4-R10,LR} 128 | SUBS R2, R2, #0x20 129 | BCC __ctr_memcpy_cache_exit 130 | LDMIA R1!, {R3-R6} 131 | PLD [R1,#0x40] 132 | LDMIA R1!, {R7-R10} 133 | __ctr_memcpy_cache_loop: 134 | STMIA R0!, {R3-R6} 135 | SUBS R2, R2, #0x20 136 | STMIA R0!, {R7-R10} 137 | LDMCSIA R1!, {R3-R6} 138 | PLD [R1,#0x40] 139 | LDMCSIA R1!, {R7-R10} 140 | BCS __ctr_memcpy_cache_loop 141 | __ctr_memcpy_cache_exit: 142 | MOVS R12, R2,LSL#28 143 | LDMCSIA R1!, {R3,R4,R12,LR} 144 | STMCSIA R0!, {R3,R4,R12,LR} 145 | LDMMIIA R1!, {R3,R4} 146 | STMMIIA R0!, {R3,R4} 147 | LDMFD SP!, {R4-R10,LR} 148 | MOVS R12, R2,LSL#30 149 | LDRCS R3, [R1],#4 150 | STRCS R3, [R0],#4 151 | BXEQ LR 152 | MOVS R2, R2,LSL#31 153 | LDRCSH R3, [R1],#2 154 | LDRMIB R2, [R1],#1 155 | STRCSH R3, [R0],#2 156 | STRMIB R2, [R0],#1 157 | BX LR 158 | 159 | .type __ctr_memcpy, %function 160 | __ctr_memcpy: 161 | CMP R2, #3 162 | BLS __byte_copy 163 | ANDS R12, R0, #3 164 | BEQ __ctr_memcpy_w 165 | LDRB R3, [R1],#1 166 | CMP R12, #2 167 | ADD R2, R2, R12 168 | LDRLSB R12, [R1],#1 169 | STRB R3, [R0],#1 170 | LDRCCB R3, [R1],#1 171 | STRLSB R12, [R0],#1 172 | SUB R2, R2, #4 173 | STRCCB R3, [R0],#1 174 | __ctr_memcpy_w: 175 | ANDS R3, R1, #3 176 | BEQ __ctr_memcpy_cache 177 | SUBS R2, R2, #8 178 | __u32_copy: 179 | BCC __u32_copy_last 180 | LDR R3, [R1],#4 181 | SUBS R2, R2, #8 182 | LDR R12, [R1],#4 183 | STMIA R0!, {R3,R12} 184 | B __u32_copy 185 | __u32_copy_last: 186 | ADDS R2, R2, #4 187 | LDRPL R3, [R1],#4 188 | STRPL R3, [R0],#4 189 | NOP 190 | __byte_copy: 191 | MOVS R2, R2,LSL#31 192 | LDRCSB R3, [R1],#1 193 | LDRCSB R12, [R1],#1 194 | LDRMIB R2, [R1],#1 195 | STRCSB R3, [R0],#1 196 | STRCSB R12, [R0],#1 197 | STRMIB R2, [R0],#1 198 | BX LR 199 | 200 | .global memcpy_ctr 201 | .type memcpy_ctr, %function 202 | memcpy_ctr: 203 | STMFD SP!, {R0,LR} 204 | BL __ctr_memcpy 205 | LDMFD SP!, {R0,PC} 206 | 207 | .global waitKeysDelay3 208 | .type waitKeysDelay3, %function 209 | waitKeysDelay3: 210 | mov r0, #3145728 211 | l3: 212 | subs r0, r0, #1 213 | bne l3 214 | bx lr 215 | 216 | .global waitKeysDelay 217 | .type waitKeysDelay, %function 218 | waitKeysDelay: 219 | mov r0, #1048576 220 | l: 221 | subs r0, r0, #1 222 | bne l 223 | bx lr 224 | -------------------------------------------------------------------------------- /source/nwm/main_nwm.c: -------------------------------------------------------------------------------- 1 | #include "global.h" 2 | 3 | #include "3ds/srv.h" 4 | #include "3ds/ipc.h" 5 | #include "3ds/result.h" 6 | #include "3ds/services/ptmsysm.h" 7 | #include "3ds/allocator/mappable.h" 8 | #include "3ds/os.h" 9 | 10 | #include "poll.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | sendPacketTypedef nwmSendPacket; 17 | static RT_HOOK nwmValParamHook; 18 | 19 | static Handle nwmReadyEvent; 20 | 21 | extern char *fake_heap_start; 22 | extern char *fake_heap_end; 23 | void __system_initSyscalls(void); 24 | Result __sync_init(void); 25 | void mainThread(void *) { 26 | __system_initSyscalls(); 27 | s32 ret; 28 | ret = __sync_init(); 29 | if (ret != 0) { 30 | nsDbgPrint("sync init failed: %08"PRIx32"\n", ret); 31 | goto final; 32 | } 33 | fake_heap_start = (void *)plgRequestMemory(NWM_HEAP_SIZE); 34 | if (!fake_heap_start) { 35 | goto final; 36 | } 37 | fake_heap_end = fake_heap_start + NWM_HEAP_SIZE; 38 | mappableInit(OS_MAP_AREA_BEGIN, OS_MAP_AREA_END); 39 | 40 | ret = srvInit(); 41 | if (ret != 0) { 42 | showDbg("srvInit failed: %08"PRIx32, ret); 43 | goto final; 44 | } 45 | 46 | ret = nsStartup(); 47 | if (ret != 0) { 48 | disp(100, DBG_CL_USE_DBG_FAIL); 49 | } else { 50 | disp(100, DBG_CL_USE_DBG); 51 | } 52 | disp(100, DBG_CL_INFO); 53 | 54 | final: 55 | if (nwmReadyEvent) { 56 | ret = svcSignalEvent(nwmReadyEvent); 57 | if (ret != 0) { 58 | showDbg("nwm payload init sync error: %08"PRIx32"\n", ret); 59 | } 60 | } 61 | 62 | svcExitThread(); 63 | } 64 | 65 | static u32 nwmReadyDone; 66 | 67 | static int nwmValParamCallback(u8 *buf, int) { 68 | if (!ATSR(&nwmReadyDone)) { 69 | if (nwmReadyEvent) { 70 | if (svcWaitSynchronization(nwmReadyEvent, NWM_INIT_READY_TIMEOUT) != 0) { 71 | disp(100, DBG_CL_MSG); 72 | } 73 | svcCloseHandle(nwmReadyEvent); 74 | nwmReadyEvent = 0; 75 | } 76 | 77 | rpStartup(buf); 78 | 79 | ACR(&nwmReadyDone); 80 | } 81 | return 0; 82 | } 83 | 84 | static RT_HOOK nwmRecvNotificationHook; 85 | static Handle *nwmSrvHandle = (Handle *)0x001547fc; 86 | 87 | void nwmPause(bool); 88 | void nwmUnpause(); 89 | 90 | static Result nwmSrvReceiveNotification(u32 *notificationIdOut) { 91 | Result rc = 0; 92 | u32 *cmdbuf = getThreadCommandBuffer(); 93 | 94 | cmdbuf[0] = IPC_MakeHeader(0xB, 0, 0); // 0xB0000 95 | 96 | rc = svcSendSyncRequest(*nwmSrvHandle); 97 | rc = R_SUCCEEDED(rc) ? cmdbuf[1] : rc; 98 | if (notificationIdOut) 99 | *notificationIdOut = R_SUCCEEDED(rc) ? cmdbuf[2] : 0; 100 | 101 | return rc; 102 | } 103 | 104 | static Result nwmRecvNotificationCallback(u32* notificationIdOut) { 105 | Result res = nwmSrvReceiveNotification(notificationIdOut); 106 | if (res != 0) 107 | return res; 108 | 109 | // showDbg("notification %"PRIx32" received", *notificationIdOut); 110 | switch (*notificationIdOut) { 111 | case PTMNOTIFID_FULLY_WAKING_UP: 112 | nwmUnpause(); 113 | break; 114 | case PTMNOTIFID_SLEEP_ALLOWED: 115 | nwmPause(true); 116 | break; 117 | case PTMNOTIFID_SHUTDOWN: 118 | nwmPause(false); 119 | return -1; 120 | default: 121 | break; 122 | } 123 | return res; 124 | } 125 | 126 | typedef Result (*srvSubscribeTypedef)(u32 notificationId); 127 | 128 | static void nwmNotificationHook(void) { 129 | srvSubscribeTypedef nwmSrvSubscribe; 130 | { 131 | u8 desiredHeader[] = { 132 | 0x1c, 0xb5, 0x04, 0x46, 0x1f, 0xf0, 0x0e, 0xee, 133 | }; 134 | u32 remotePC = 0x0010455c; 135 | u8 buf[sizeof(desiredHeader)] = { 0 }; 136 | 137 | s32 ret = copyRemoteMemory(CUR_PROCESS_HANDLE, buf, CUR_PROCESS_HANDLE, (void *)remotePC, sizeof(desiredHeader)); 138 | if (ret != 0) { 139 | nsDbgPrint("Read NWM memory at %08"PRIx32" failed: %08"PRIx32"\n", remotePC, ret); 140 | goto final; 141 | } 142 | 143 | if (memcmp(buf, desiredHeader, sizeof(desiredHeader)) != 0) { 144 | nsDbgPrint("Unexpected NWM memory content\n"); 145 | goto final; 146 | } 147 | 148 | nwmSrvSubscribe = (srvSubscribeTypedef)(remotePC + 1); 149 | } 150 | 151 | u8 desiredHeader[] = { 152 | 0x1c, 0xb5, 0x04, 0x46, 0x22, 0xf0, 0xf2, 0xee, 153 | }; 154 | u32 remotePC = 0x00101394; 155 | u8 buf[sizeof(desiredHeader)] = { 0 }; 156 | 157 | s32 ret = copyRemoteMemory(CUR_PROCESS_HANDLE, buf, CUR_PROCESS_HANDLE, (void *)remotePC, sizeof(desiredHeader)); 158 | if (ret != 0) { 159 | nsDbgPrint("Read NWM memory at %08"PRIx32" failed: %08"PRIx32"\n", remotePC, ret); 160 | goto final; 161 | } 162 | 163 | if (memcmp(buf, desiredHeader, sizeof(desiredHeader)) != 0) { 164 | nsDbgPrint("Unexpected NWM memory content\n"); 165 | goto final; 166 | } 167 | 168 | u32 notifications[] = { 169 | PTMNOTIFID_SHUTDOWN, 170 | }; 171 | for (unsigned i = 0; i < sizeof(notifications) / sizeof(*notifications); ++i) { 172 | ret = nwmSrvSubscribe(notifications[i]); 173 | if (ret != 0) { 174 | nsDbgPrint("NWM %"PRIx32" notification subscribe failed: %08"PRIx32"\n", notifications[i], ret); 175 | } 176 | } 177 | 178 | rtInitHookThumb(&nwmRecvNotificationHook, remotePC, (u32)nwmRecvNotificationCallback); 179 | rtEnableHook(&nwmRecvNotificationHook); 180 | 181 | final: 182 | return; 183 | } 184 | 185 | void mainPre(void) { 186 | if (svcCreateEvent(&nwmReadyEvent, RESET_ONESHOT) != 0) { 187 | nwmReadyEvent = 0; 188 | disp(100, DBG_CL_MSG); 189 | } 190 | nwmSendPacket = (sendPacketTypedef)nsConfig->startupInfo[12]; 191 | rtInitHookThumb(&nwmValParamHook, nsConfig->startupInfo[11], (u32)nwmValParamCallback); 192 | rtEnableHook(&nwmValParamHook); 193 | 194 | nwmNotificationHook(); 195 | } 196 | 197 | void _ReturnToUser(void); 198 | int setUpReturn(void) { 199 | u32 *buf = (void *)_ReturnToUser; 200 | buf[0] = 0xe3b00000; // return 0; 201 | buf[1] = 0xe12fff1e; 202 | 203 | return rtFlushInstructionCache((void *)_ReturnToUser, 8); 204 | } 205 | 206 | u32 __apt_appid; 207 | u32 __system_runflags; 208 | 209 | u8 nsHasPoll2 = 1; 210 | static s32 nwm_recv_sock = -1; 211 | 212 | void nsPoll2End() { 213 | if (nwm_recv_sock >= 0) { 214 | closesocket(nwm_recv_sock); 215 | nwm_recv_sock = -1; 216 | } 217 | } 218 | 219 | int nsPoll2(int s) { 220 | if (nwm_recv_sock < 0) { 221 | nwm_recv_sock = socket(AF_INET, SOCK_DGRAM, 0); 222 | if (nwm_recv_sock < 0) { 223 | showDbg("nwm socket failed: %08"PRIx32, (u32)errno); 224 | return -1; 225 | } 226 | 227 | struct sockaddr_in sai = {0}; 228 | sai.sin_family = AF_INET; 229 | sai.sin_port = htons(RP_SRC_PORT); 230 | sai.sin_addr.s_addr = htonl(INADDR_ANY); 231 | 232 | int ret = bind(nwm_recv_sock, (struct sockaddr *)&sai, sizeof(sai)); 233 | if (ret < 0) { 234 | showDbg("nwm_recv_sock bind failed: %08"PRIx32, (u32)errno); 235 | nsPoll2End(); 236 | return -1; 237 | } 238 | 239 | s32 tmp = fcntl(nwm_recv_sock, F_GETFL); 240 | fcntl(nwm_recv_sock, F_SETFL, tmp | O_NONBLOCK); 241 | } 242 | 243 | struct pollfd pi[2]; 244 | int nready; 245 | 246 | pi[0].fd = s; 247 | pi[1].fd = nwm_recv_sock; 248 | pi[1].events = pi[0].events = POLLIN; 249 | pi[1].revents = pi[0].revents = 0; 250 | nready = poll2(pi, 2, -1); 251 | if (nready == 0) 252 | return 0; 253 | else if (nready < 0) { 254 | showDbg("socket poll failed: %08"PRIx32, (u32)errno); 255 | return -1; 256 | } 257 | if (pi[1].revents & (POLLIN)) { 258 | if (nsControlRecv(nwm_recv_sock) < 0) { 259 | // nsDbgPrint("nsControlRecv failed\n"); 260 | nsPoll2End(); 261 | } 262 | } 263 | if (pi[0].revents & (POLLIN | POLLHUP)) 264 | return 1; 265 | 266 | return 0; 267 | } 268 | -------------------------------------------------------------------------------- /source/nwm_misc/FecalCommon.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 Christopher A. Taylor. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of Fecal nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #include "FecalCommon.h" 30 | 31 | namespace fecal { 32 | 33 | 34 | //------------------------------------------------------------------------------ 35 | // AppDataWindow 36 | 37 | bool AppDataWindow::SetParameters(unsigned input_count, uint64_t total_bytes) 38 | { 39 | if (input_count <= 0 || total_bytes < input_count) 40 | { 41 | FECAL_DEBUG_BREAK; // Invalid input 42 | return false; 43 | } 44 | 45 | InputCount = input_count; 46 | TotalBytes = total_bytes; 47 | 48 | FinalBytes = static_cast(total_bytes % SymbolSize); 49 | if (FinalBytes <= 0) 50 | FinalBytes = SymbolSize; 51 | 52 | FECAL_DEBUG_ASSERT(SymbolSize >= FinalBytes && FinalBytes != 0); 53 | 54 | return true; 55 | } 56 | 57 | 58 | //------------------------------------------------------------------------------ 59 | // AlignedDataBuffer 60 | 61 | bool AlignedDataBuffer::Allocate(unsigned bytes) 62 | { 63 | if (bytes == SymbolSize) { 64 | memset(DataMem, 0, sizeof(DataMem)); 65 | uint8_t *data = DataMem; 66 | unsigned offset = (unsigned)((uintptr_t)data % kAlignmentBytes); 67 | data += kAlignmentBytes - offset; 68 | Data = data; 69 | return true; 70 | } 71 | return false; 72 | } 73 | 74 | 75 | //------------------------------------------------------------------------------ 76 | // GrowingAlignedByteMatrix 77 | 78 | 79 | } // namespace fecal 80 | -------------------------------------------------------------------------------- /source/nwm_misc/FecalEncoder.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 Christopher A. Taylor. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of Fecal nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #include "FecalEncoder.h" 30 | #include "constants.h" 31 | 32 | namespace fecal { 33 | 34 | 35 | //------------------------------------------------------------------------------ 36 | // EncoderAppDataWindow 37 | 38 | 39 | //------------------------------------------------------------------------------ 40 | // Encoder 41 | 42 | // This optimization speeds up encoding by about 5% 43 | #ifdef FECAL_ADD2_OPT 44 | #define FECAL_ADD2_ENC_SETUP_OPT 45 | #endif 46 | 47 | FecalResult Encoder::Initialize(unsigned input_count, void* const * const input_data, uint64_t total_bytes) 48 | { 49 | // Validate input and set parameters 50 | if (!Window.SetParameters(input_count, total_bytes)) 51 | { 52 | FECAL_DEBUG_BREAK; // Invalid input 53 | return Fecal_InvalidInput; 54 | } 55 | Window.OriginalData = input_data; 56 | 57 | const unsigned symbolBytes = SymbolSize; 58 | 59 | // Allocate lane sums 60 | for (unsigned laneIndex = 0; laneIndex < kColumnLaneCount; ++laneIndex) 61 | { 62 | for (unsigned sumIndex = 0; sumIndex < kColumnSumCount; ++sumIndex) 63 | { 64 | if (!LaneSums[laneIndex][sumIndex].Allocate(symbolBytes)) 65 | return Fecal_OutOfMemory; 66 | 67 | // Clear memory in each lane sum 68 | } 69 | } 70 | 71 | // Allocate workspace 72 | if (!ProductWorkspace.Allocate(symbolBytes)) 73 | return Fecal_OutOfMemory; 74 | 75 | // TBD: Unroll first set of 8 lanes to avoid the extra memset above? 76 | // TBD: Use GetLaneSum() approach do to minimal work for small output? 77 | 78 | #ifdef FECAL_ADD2_ENC_SETUP_OPT 79 | for (unsigned laneIndex = 0; laneIndex < kColumnLaneCount; ++laneIndex) 80 | { 81 | // Sum[0] += Data 82 | XORSummer sum; 83 | sum.Initialize(LaneSums[laneIndex][0].Data, symbolBytes); 84 | 85 | const unsigned columnEnd = input_count - 1; 86 | 87 | for (unsigned column = laneIndex; column < columnEnd; column += kColumnLaneCount) 88 | { 89 | const uint8_t* columnData = reinterpret_cast(input_data[column]); 90 | sum.Add(columnData); 91 | } 92 | 93 | if ((columnEnd % kColumnLaneCount) == laneIndex) 94 | { 95 | const uint8_t* columnData = reinterpret_cast(input_data[columnEnd]); 96 | gf256_add_mem(LaneSums[laneIndex][0].Data, columnData, Window.FinalBytes); 97 | } 98 | 99 | sum.Finalize(); 100 | } 101 | #endif 102 | 103 | // For each input column: 104 | for (unsigned column = 0; column < input_count; ++column) 105 | { 106 | const uint8_t* columnData = reinterpret_cast(input_data[column]); 107 | const unsigned columnBytes = Window.GetColumnBytes(column); 108 | const unsigned laneIndex = column % kColumnLaneCount; 109 | const uint8_t CX = GetColumnValue(column); 110 | const uint8_t CX2 = gf256_sqr(CX); 111 | 112 | #ifndef FECAL_ADD2_ENC_SETUP_OPT 113 | // Sum[0] += Data 114 | gf256_add_mem(LaneSums[laneIndex][0].Data, columnData, columnBytes); 115 | #endif 116 | 117 | // Sum[1] += CX * Data 118 | gf256_muladd_mem(LaneSums[laneIndex][1].Data, CX, columnData, columnBytes); 119 | 120 | // Sum[2] += CX^2 * Data 121 | gf256_muladd_mem(LaneSums[laneIndex][2].Data, CX2, columnData, columnBytes); 122 | } 123 | 124 | return Fecal_Success; 125 | 126 | static_assert(kColumnSumCount == 3, "Update this"); 127 | } 128 | 129 | FecalResult Encoder::Encode(FecalSymbol& symbol) 130 | { 131 | // If encoder is not initialized: 132 | if (!ProductWorkspace.Data) 133 | return Fecal_InvalidInput; 134 | 135 | const unsigned symbolBytes = SymbolSize; 136 | if (symbol.Bytes != symbolBytes) 137 | return Fecal_InvalidInput; 138 | 139 | // Load parameters 140 | const unsigned count = Window.InputCount; 141 | uint8_t* outputSum = reinterpret_cast( symbol.Data ); 142 | uint8_t* outputProduct = ProductWorkspace.Data; 143 | 144 | const unsigned row = symbol.Index; 145 | 146 | // Initialize LDPC 147 | PCGRandom prng; 148 | prng.Seed(row, count); 149 | 150 | // Accumulate original data into the two sums 151 | const unsigned pairCount = (Window.InputCount + kPairAddRate - 1) / kPairAddRate; 152 | // Unrolled first loop: 153 | { 154 | const unsigned element1 = prng.Next() % count; 155 | const uint8_t* original1 = reinterpret_cast(Window.OriginalData[element1]); 156 | 157 | const unsigned elementRX = prng.Next() % count; 158 | const uint8_t* originalRX = reinterpret_cast(Window.OriginalData[elementRX]); 159 | 160 | // Sum = Original[element1] 161 | if (Window.IsFinalColumn(element1)) 162 | { 163 | memcpy(outputSum, original1, Window.FinalBytes); 164 | memset(outputSum + Window.FinalBytes, 0, symbolBytes - Window.FinalBytes); 165 | } 166 | else 167 | memcpy(outputSum, original1, symbolBytes); 168 | 169 | // Product = Original[elementRX] 170 | if (Window.IsFinalColumn(elementRX)) 171 | { 172 | memcpy(outputProduct, originalRX, Window.FinalBytes); 173 | memset(outputProduct + Window.FinalBytes, 0, symbolBytes - Window.FinalBytes); 174 | } 175 | else 176 | memcpy(outputProduct, originalRX, symbolBytes); 177 | } 178 | 179 | XORSummer sum; 180 | sum.Initialize(outputSum, symbolBytes); 181 | XORSummer prod; 182 | prod.Initialize(outputProduct, symbolBytes); 183 | 184 | for (unsigned i = 1; i < pairCount; ++i) 185 | { 186 | const unsigned element1 = prng.Next() % count; 187 | const uint8_t* original1 = reinterpret_cast(Window.OriginalData[element1]); 188 | 189 | const unsigned elementRX = prng.Next() % count; 190 | const uint8_t* originalRX = reinterpret_cast(Window.OriginalData[elementRX]); 191 | 192 | // Sum += Original[element1] 193 | if (Window.IsFinalColumn(element1)) 194 | gf256_add_mem(outputSum, original1, Window.FinalBytes); 195 | else 196 | sum.Add(original1); 197 | 198 | // Product += Original[elementRX] 199 | if (Window.IsFinalColumn(elementRX)) 200 | gf256_add_mem(outputProduct, originalRX, Window.FinalBytes); 201 | else 202 | prod.Add(originalRX); 203 | } 204 | 205 | // For each lane: 206 | for (unsigned laneIndex = 0; laneIndex < kColumnLaneCount; ++laneIndex) 207 | { 208 | // Compute the operations to run for this lane and row 209 | unsigned opcode = GetRowOpcode(laneIndex, row); 210 | 211 | // Sum += Random Lanes 212 | unsigned mask = 1; 213 | for (unsigned sumIndex = 0; sumIndex < kColumnSumCount; ++sumIndex, mask <<= 1) 214 | if (opcode & mask) 215 | sum.Add(LaneSums[laneIndex][sumIndex].Data); 216 | 217 | // Product += Random Lanes 218 | for (unsigned sumIndex = 0; sumIndex < kColumnSumCount; ++sumIndex, mask <<= 1) 219 | if (opcode & mask) 220 | prod.Add(LaneSums[laneIndex][sumIndex].Data); 221 | } 222 | 223 | sum.Finalize(); 224 | prod.Finalize(); 225 | 226 | // Sum += RX * Product 227 | gf256_muladd_mem(outputSum, GetRowValue(row), outputProduct, symbolBytes); 228 | 229 | return Fecal_Success; 230 | } 231 | 232 | 233 | } // namespace fecal 234 | -------------------------------------------------------------------------------- /source/nwm_misc/FecalEncoder.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 Christopher A. Taylor. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of Fecal nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #pragma once 30 | 31 | /* 32 | Encoder 33 | 34 | The encoder builds up sums of input data on Initialize(). 35 | 36 | When Encode() is called it will combine these sums in a deterministic way. 37 | 38 | Encode returns a pointer to the Sum workspace. 39 | */ 40 | 41 | #include "FecalCommon.h" 42 | 43 | namespace fecal { 44 | 45 | 46 | //------------------------------------------------------------------------------ 47 | // EncoderAppDataWindow 48 | 49 | // Encoder-specialized app data window 50 | struct EncoderAppDataWindow : AppDataWindow 51 | { 52 | // Original data 53 | void* const* OriginalData; 54 | }; 55 | 56 | 57 | //------------------------------------------------------------------------------ 58 | // Encoder 59 | 60 | class Encoder 61 | { 62 | public: 63 | // Initialize the encoder 64 | FecalResult Initialize(unsigned input_count, void* const * const input_data, uint64_t total_bytes); 65 | 66 | // Generate the next recovery packet for the data 67 | FecalResult Encode(FecalSymbol& symbol); 68 | 69 | protected: 70 | // Application data set 71 | EncoderAppDataWindow Window; 72 | 73 | // Sums for each lane 74 | AlignedDataBuffer LaneSums[kColumnLaneCount][kColumnSumCount]; 75 | 76 | // Output workspace 77 | AlignedDataBuffer ProductWorkspace; 78 | }; 79 | 80 | 81 | } // namespace fecal 82 | -------------------------------------------------------------------------------- /source/nwm_misc/fecal.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 Christopher A. Taylor. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of Fecal nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #include "fecal.h" 30 | #include "gf256.h" 31 | #include "FecalEncoder.h" 32 | 33 | extern "C" { 34 | 35 | //------------------------------------------------------------------------------ 36 | // Initialization API 37 | 38 | static bool m_Initialized = false; 39 | 40 | FECAL_EXPORT int fecal_init_(int version) 41 | { 42 | if (version != FECAL_VERSION) 43 | return Fecal_InvalidInput; 44 | 45 | if (0 != gf256_init()) 46 | return Fecal_Platform; 47 | 48 | m_Initialized = true; 49 | return Fecal_Success; 50 | } 51 | 52 | 53 | //------------------------------------------------------------------------------ 54 | // Encoder API 55 | 56 | FECAL_EXPORT int fecal_encoder_size(void) 57 | { 58 | return sizeof(fecal::Encoder); 59 | } 60 | 61 | FECAL_EXPORT int fecal_encoder_align(void) 62 | { 63 | return alignof(fecal::Encoder); 64 | } 65 | 66 | FECAL_EXPORT int fecal_encoder_init(FecalEncoder encoder, unsigned input_count, void* const * const input_data, uint64_t total_bytes) 67 | { 68 | if (input_count <= 0 || !input_data || total_bytes < input_count) 69 | { 70 | FECAL_DEBUG_BREAK; // Invalid input 71 | return Fecal_InvalidInput; 72 | } 73 | 74 | FECAL_DEBUG_ASSERT(m_Initialized); // Must call fecal_init() first 75 | if (!m_Initialized) 76 | return Fecal_InvalidInput; 77 | 78 | return reinterpret_cast(encoder)->Initialize(input_count, input_data, total_bytes); 79 | } 80 | 81 | FECAL_EXPORT int fecal_encode(FecalEncoder encoder_v, FecalSymbol* symbol) 82 | { 83 | fecal::Encoder* encoder = reinterpret_cast( encoder_v ); 84 | if (!encoder || !symbol) 85 | return Fecal_InvalidInput; 86 | 87 | return encoder->Encode(*symbol); 88 | } 89 | 90 | typedef fecal::CustomBitSet<1 << 12> BitSet4096Impl; 91 | static_assert(sizeof(BitSet4096Impl) == sizeof(BitSet4096Mem)); 92 | 93 | FECAL_EXPORT void rp_arq_bitset_clear_all(BitSet4096 bs) 94 | { 95 | ((BitSet4096Impl *)bs)->ClearAll(); 96 | } 97 | 98 | FECAL_EXPORT void rp_arq_bitset_set(BitSet4096 bs, unsigned b) 99 | { 100 | ((BitSet4096Impl *)bs)->Set(b); 101 | } 102 | 103 | FECAL_EXPORT void rp_arq_bitset_clear(BitSet4096 bs, unsigned b) 104 | { 105 | ((BitSet4096Impl *)bs)->Clear(b); 106 | } 107 | 108 | FECAL_EXPORT bool rp_arq_bitset_check(BitSet4096 bs, unsigned b) 109 | { 110 | return ((BitSet4096Impl *)bs)->Check(b); 111 | } 112 | 113 | FECAL_EXPORT bool rp_arq_bitset_check_n_wrapped(BitSet4096 bs, unsigned b, unsigned n) 114 | { 115 | if (b + n > BitSet4096Impl::kValidBits) { 116 | unsigned n_1 = n - (BitSet4096Impl::kValidBits - b); 117 | return 118 | ((BitSet4096Impl *)bs)->FindFirstSet(b, BitSet4096Impl::kValidBits) < BitSet4096Impl::kValidBits || 119 | ((BitSet4096Impl *)bs)->FindFirstSet(0, n_1) < n_1; 120 | } else { 121 | return ((BitSet4096Impl *)bs)->FindFirstSet(b, b + n) < b + n; 122 | } 123 | } 124 | 125 | FECAL_EXPORT unsigned rp_arq_bitset_ffs_n_wrapped(BitSet4096 bs, unsigned b, unsigned n) 126 | { 127 | if (b + n > BitSet4096Impl::kValidBits) { 128 | unsigned n_1 = n - (BitSet4096Impl::kValidBits - b); 129 | unsigned ret = ((BitSet4096Impl *)bs)->FindFirstSet(b, BitSet4096Impl::kValidBits); 130 | if (ret < BitSet4096Impl::kValidBits) { 131 | return ret; 132 | } 133 | return ((BitSet4096Impl *)bs)->FindFirstSet(0, n_1); 134 | } else { 135 | return ((BitSet4096Impl *)bs)->FindFirstSet(b, b + n); 136 | } 137 | } 138 | 139 | FECAL_EXPORT bool rp_arq_bitset_check_all_set_n_wrapped(BitSet4096 bs, unsigned b, unsigned n) 140 | { 141 | if (b + n > BitSet4096Impl::kValidBits) { 142 | unsigned n_0 = BitSet4096Impl::kValidBits - b; 143 | unsigned n_1 = n - n_0; 144 | return 145 | ((BitSet4096Impl *)bs)->RangePopcount(b, BitSet4096Impl::kValidBits) == n_0 && 146 | ((BitSet4096Impl *)bs)->RangePopcount(0, n_1) == n_1; 147 | } else { 148 | return ((BitSet4096Impl *)bs)->RangePopcount(b, b + n) == n; 149 | } 150 | } 151 | 152 | FECAL_EXPORT unsigned rp_arq_bitset_ffc_n_wrapped(BitSet4096 bs, unsigned b, unsigned n) 153 | { 154 | if (b + n > BitSet4096Impl::kValidBits) { 155 | unsigned n_1 = n - (BitSet4096Impl::kValidBits - b); 156 | unsigned ret = ((BitSet4096Impl *)bs)->FindFirstClear(b); 157 | if (ret < BitSet4096Impl::kValidBits) { 158 | return ret; 159 | } 160 | return (ret = ((BitSet4096Impl *)bs)->FindFirstClear(0)), (ret < n_1 ? ret : n_1); 161 | } else { 162 | unsigned ret = ((BitSet4096Impl *)bs)->FindFirstClear(b); 163 | return ret < b + n ? ret : b + n; 164 | } 165 | } 166 | 167 | 168 | } // extern "C" 169 | -------------------------------------------------------------------------------- /source/nwm_misc/fecal.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 Christopher A. Taylor. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of Fecal nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef CAT_FECAL_H 30 | #define CAT_FECAL_H 31 | 32 | /* 33 | FEC-AL: Forward Error Correction at the Application Layer 34 | Block erasure code based on math from the Siamese library. 35 | */ 36 | 37 | // Library version 38 | #define FECAL_VERSION 2 39 | 40 | // Tweak if the functions are exported or statically linked 41 | //#define FECAL_DLL /* Defined when building/linking as DLL */ 42 | //#define FECAL_BUILDING /* Defined by the library makefile */ 43 | 44 | #if defined(FECAL_BUILDING) 45 | # if defined(FECAL_DLL) 46 | #define FECAL_EXPORT __declspec(dllexport) 47 | # else 48 | #define FECAL_EXPORT 49 | # endif 50 | #else 51 | # if defined(FECAL_DLL) 52 | #define FECAL_EXPORT __declspec(dllimport) 53 | # else 54 | #define FECAL_EXPORT extern 55 | # endif 56 | #endif 57 | 58 | #include 59 | 60 | 61 | #ifdef __cplusplus 62 | extern "C" { 63 | #endif 64 | 65 | 66 | //------------------------------------------------------------------------------ 67 | // Initialization API 68 | // 69 | // Perform static initialization for the library, verifying that the platform 70 | // is supported. 71 | // 72 | // Returns 0 on success and other values on failure. 73 | 74 | FECAL_EXPORT int fecal_init_(int version); 75 | #define fecal_init() fecal_init_(FECAL_VERSION) 76 | 77 | 78 | //------------------------------------------------------------------------------ 79 | // Shared Constants / Datatypes 80 | 81 | // Results 82 | typedef enum FecalResultT 83 | { 84 | Fecal_NeedMoreData = 1, // More data is needed for this operation to succeed 85 | 86 | Fecal_Success = 0, 87 | 88 | Fecal_InvalidInput = -1, // A function parameter was invalid 89 | Fecal_Platform = -2, // Platform is unsupported 90 | Fecal_OutOfMemory = -3, // Out of memory error occurred 91 | Fecal_Unexpected = -4, // Unexpected error - Software bug? 92 | } FecalResult; 93 | 94 | // Encoder and Decoder object types 95 | typedef struct FecalEncoderImpl { int impl; }*FecalEncoder; 96 | 97 | // Data or Recovery symbol 98 | typedef struct FecalSymbolT 99 | { 100 | // User-provided data pointer allocated by application. 101 | void* Data; 102 | 103 | // User-provided number of bytes in the data buffer, for validation. 104 | unsigned Bytes; 105 | 106 | // Zero-based index in the data array, 107 | // or a larger number for recovery data. 108 | unsigned Index; 109 | } FecalSymbol; 110 | 111 | // Recovered data 112 | typedef struct RecoveredSymbolsT 113 | { 114 | // Array of symbols 115 | FecalSymbol* Symbols; 116 | 117 | // Number of symbols in the array 118 | unsigned Count; 119 | } RecoveredSymbols; 120 | 121 | 122 | //------------------------------------------------------------------------------ 123 | // Encoder API 124 | 125 | FECAL_EXPORT int fecal_encoder_size(void); 126 | FECAL_EXPORT int fecal_encoder_align(void); 127 | 128 | /* 129 | fecal_encoder_create() 130 | 131 | Create an encoder and set the input data. 132 | 133 | input_count: Number of input_data[] buffers provided. 134 | input_data: Array of pointers to input data. 135 | total_bytes: Sum of the total bytes in all buffers. 136 | 137 | Buffer data must be available until the decoder is freed with fecal_free(). 138 | Buffer data does not need to be aligned. 139 | Buffer data will not be modified, only read. 140 | 141 | Each buffer should have the same number of bytes except for the last one, 142 | which can be shorter. 143 | 144 | Let symbol_bytes = The number of bytes in each input_data buffer: 145 | 146 | input_count = static_cast( 147 | (total_bytes + symbol_bytes - 1) / symbol_bytes); 148 | 149 | Or if the number of pieces is known: 150 | 151 | symbol_bytes = static_cast( 152 | (total_bytes + input_count - 1) / input_count); 153 | 154 | Let final_bytes = The final piece of input data size in bytes: 155 | 156 | final_bytes = static_cast(total_bytes % symbol_bytes); 157 | if (final_bytes <= 0) 158 | final_bytes = symbol_bytes; 159 | 160 | Returns NULL on failure. 161 | */ 162 | FECAL_EXPORT int fecal_encoder_init(FecalEncoder encoder, unsigned input_count, void* const * const input_data, uint64_t total_bytes); 163 | 164 | /* 165 | fecal_encode() 166 | 167 | Generate a recovery symbol. 168 | 169 | encoder: Encoder from fecal_encoder_create(). 170 | symbol->Index: Application provided recovery symbol index starting from 0. 171 | symbol->Data: Application provided buffer to write the symbol to. 172 | symbol->Bytes: Application provided number of bytes in the symbol buffer. 173 | 174 | Given total_bytes and input_count from fecal_encoder_create(): 175 | 176 | symbol->Bytes = static_cast( 177 | (total_bytes + input_count - 1) / input_count); 178 | 179 | Returns Fecal_Success on success. 180 | Returns Fecal_InvalidInput if the symbol parameter was invalid, or the 181 | codec is not initialized yet. 182 | */ 183 | FECAL_EXPORT int fecal_encode(FecalEncoder encoder, FecalSymbol* symbol); 184 | 185 | /* 186 | fecal_free() 187 | 188 | Free memory associated with the created encoder or decoder. 189 | 190 | codec: Pointer returned by fecal_encoder_create() or fecal_decoder_create() 191 | */ 192 | 193 | #define BitSet4096ValidBits (1 << 12) 194 | #define BitSetWordT uint64_t 195 | #define BitSetWordBits (sizeof(BitSetWordT) * 8) 196 | #define BitSet4096Words ((BitSet4096ValidBits + BitSetWordBits - 1) / BitSetWordBits) 197 | 198 | typedef struct BitSet4096Mem { BitSetWordT Words[BitSet4096Words]; }*BitSet4096; 199 | 200 | FECAL_EXPORT void rp_arq_bitset_clear_all(BitSet4096); 201 | FECAL_EXPORT void rp_arq_bitset_set(BitSet4096, unsigned); 202 | FECAL_EXPORT void rp_arq_bitset_clear(BitSet4096, unsigned); 203 | FECAL_EXPORT bool rp_arq_bitset_check(BitSet4096, unsigned); 204 | FECAL_EXPORT bool rp_arq_bitset_check_n_wrapped(BitSet4096, unsigned, unsigned); 205 | FECAL_EXPORT unsigned rp_arq_bitset_ffs_n_wrapped(BitSet4096, unsigned, unsigned); 206 | FECAL_EXPORT bool rp_arq_bitset_check_all_set_n_wrapped(BitSet4096, unsigned, unsigned); 207 | FECAL_EXPORT unsigned rp_arq_bitset_ffc_n_wrapped(BitSet4096, unsigned, unsigned); 208 | 209 | 210 | #ifdef __cplusplus 211 | } 212 | #endif 213 | 214 | 215 | #endif // CAT_FECAL_H 216 | -------------------------------------------------------------------------------- /source/nwm_misc/mempool.c: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Adrian Reuter 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | #include "mempool.h" 29 | #include "global.h" 30 | 31 | // #define CHECK 32 | 33 | struct block { 34 | void *next; 35 | }; 36 | 37 | int mp_init(size_t bs, size_t bc, void *m, mp_pool_t *mp) 38 | { 39 | if(bs < sizeof(size_t)) { 40 | return -1; 41 | } 42 | if(bs % sizeof(size_t)) { 43 | return -1; 44 | } 45 | mp->bs = bs; 46 | mp->ul_bc = bc; 47 | mp->b = NULL; 48 | mp->ul_b = m; 49 | 50 | mp->bc = bc; 51 | mp->m = m; 52 | 53 | return 0; 54 | } 55 | 56 | void *mp_malloc(mp_pool_t *mp) 57 | { 58 | /* 59 | * 1. First we try to allocate an unlinked block 60 | * 2. In case there are no more unlinked blocks left we try to return the head from the list of free blocks 61 | * 3. Otherwise we will have to abort since there are no free blocks left 62 | */ 63 | #ifdef CHECK 64 | char *m_end = (char *)mp->m + mp->bs * mp->bc; 65 | #endif 66 | 67 | if(mp->ul_bc > 0) { 68 | mp->ul_bc--; 69 | void *b = mp->ul_b; 70 | 71 | #ifdef CHECK 72 | if (b < mp->m || b >= (void *)m_end) { 73 | showDbg("Out of range pointer for pool ul malloc %08"PRIx32" (begin %08"PRIx32" end %08"PRIx32")", (u32)b, (u32)mp->m, (u32)m_end); 74 | return NULL; 75 | } 76 | if (((char *)b - (char *)mp->m) % mp->bs) { 77 | showDbg("Mis-aligned pointer for pool ul malloc %08"PRIx32" (begin %08"PRIx32" bs %08"PRIx32")", (u32)b, (u32)mp->m, (u32)mp->bs); 78 | return NULL; 79 | } 80 | #endif 81 | 82 | mp->ul_b = (void *) (((unsigned char *) mp->ul_b) + mp->bs); 83 | #ifdef CHECK 84 | memset(b, 0xc3, mp->bs); 85 | #endif 86 | // nsDbgPrint("mp_malloc ul %08"PRIx32, (u32)b); 87 | return b; 88 | } else if(mp->b) { 89 | void *b = mp->b; 90 | 91 | #ifdef CHECK 92 | if (b < mp->m || b >= (void *)m_end) { 93 | showDbg("Out of range pointer for pool malloc %08"PRIx32" (begin %08"PRIx32" end %08"PRIx32")", (u32)b, (u32)mp->m, (u32)m_end); 94 | return NULL; 95 | } 96 | if (((char *)b - (char *)mp->m) % mp->bs) { 97 | showDbg("Mis-aligned pointer for pool malloc %08"PRIx32" (begin %08"PRIx32" bs %08"PRIx32")", (u32)b, (u32)mp->m, (u32)mp->bs); 98 | return NULL; 99 | } 100 | for (size_t i = sizeof(void *); i < mp->bs; ++i) { 101 | if (((char *)b)[i] != 0x3c) { 102 | showDbg("Use after free at %08"PRIx32" [%"PRIx32"]", (u32)b, (u32)i); 103 | return NULL; 104 | } 105 | } 106 | 107 | #endif 108 | 109 | mp->b = ((struct block *) mp->b)->next; 110 | // nsDbgPrint("mp_malloc %08"PRIx32, (u32)b); 111 | #ifdef CHECK 112 | memset(b, 0xc3, mp->bs); 113 | #endif 114 | 115 | return b; 116 | } 117 | 118 | return NULL; 119 | } 120 | 121 | int mp_free(mp_pool_t *mp, void *b) 122 | { 123 | /* 124 | * We add b as the head of the list of free blocks 125 | */ 126 | // nsDbgPrint("mp_free %08"PRIx32, (u32)b); 127 | #ifdef CHECK 128 | char *m_end = (char *)mp->m + mp->bs * mp->bc; 129 | if (b < mp->m || b >= (void *)m_end) { 130 | showDbg("Out of range pointer for pool free %08"PRIx32" (begin %08"PRIx32" end %08"PRIx32")", (u32)b, (u32)mp->m, (u32)m_end); 131 | return -1; 132 | } 133 | if (((char *)b - (char *)mp->m) % mp->bs) { 134 | showDbg("Mis-aligned pointer for pool free %08"PRIx32" (begin %08"PRIx32" bs %08"PRIx32")", (u32)b, (u32)mp->m, (u32)mp->bs); 135 | return -2; 136 | } 137 | 138 | memset(b, 0x3c, mp->bs); 139 | #endif 140 | 141 | ((struct block *) b)->next = mp->b; 142 | mp->b = b; 143 | 144 | return 0; 145 | } 146 | -------------------------------------------------------------------------------- /source/nwm_misc/mempool.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Adrian Reuter 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #ifndef MEMPOOL_H 26 | #define MEMPOOL_H 27 | 28 | #include /* size_t */ 29 | 30 | /* 31 | * mp_data contains the data of the memory pool 32 | */ 33 | struct mp_data { 34 | size_t bs; // the size of a block from the memory pool 35 | size_t ul_bc; // the count of unlinked blocks in the memory pool 36 | void *b; // a pointer to the head of the linked list of unused blocks 37 | void *ul_b; // a pointer to the first unlinked block 38 | 39 | size_t bc; 40 | void *m; 41 | }; 42 | 43 | typedef struct mp_data mp_pool_t; 44 | 45 | /* 46 | * Initialize the memory pool. 47 | * If bs is smaller than sizeof(size_t) it will set mp_error to MP_INVALID_BLOCKSIZE 48 | */ 49 | int mp_init(size_t bs, size_t bc, void *m, mp_pool_t *mp); 50 | 51 | /* 52 | * Retrieve a free block from the memory pool. 53 | * If unsuccessfull it will set mp_error to MP_OUT_OF_MEMORY 54 | */ 55 | void *mp_malloc(mp_pool_t *mp); 56 | 57 | /* 58 | * Adds a block to the list of free blocks 59 | */ 60 | int mp_free(mp_pool_t *mp, void *b); 61 | 62 | #endif /* MEMPOOL_H */ 63 | -------------------------------------------------------------------------------- /source/nwm_misc/rp_res.c: -------------------------------------------------------------------------------- 1 | #include "rp_res.h" 2 | 3 | #include "3ds/svc.h" 4 | #include "global.h" 5 | 6 | void rp_svc_increase_limits(void) { 7 | Handle resLim; 8 | Result res; 9 | if ((res = svcGetResourceLimit(&resLim, CUR_PROCESS_HANDLE))) { 10 | nsDbgPrint("svcGetResourceLimit failed\n"); 11 | return; 12 | } 13 | ResourceLimitType types[] = {RESLIMIT_MUTEX, RESLIMIT_SEMAPHORE}; 14 | int count = sizeof(types) / sizeof(types[0]); 15 | s64 values[] = {64, 128}; 16 | 17 | if ((res = svcSetResourceLimitValues(resLim, types, values, count))) { 18 | nsDbgPrint("svcSetResourceLimitValues failed\n"); 19 | } 20 | 21 | svcCloseHandle(resLim); 22 | } 23 | 24 | void rp_svc_print_limits(void) { 25 | Handle resLim; 26 | Result res; 27 | if ((res = svcGetResourceLimit(&resLim, CUR_PROCESS_HANDLE))) { 28 | nsDbgPrint("svcGetResourceLimit failed\n"); 29 | return; 30 | } 31 | ResourceLimitType types[] = {RESLIMIT_MUTEX, RESLIMIT_SEMAPHORE, RESLIMIT_EVENT, RESLIMIT_THREAD}; 32 | int count = sizeof(types) / sizeof(types[0]); 33 | const char *names[] = {"mutex", "sem", "event", "thread"}; 34 | s64 values[count]; 35 | 36 | if ((res = svcGetResourceLimitCurrentValues(values, resLim, types, count))) { 37 | nsDbgPrint("svcGetResourceLimitCurrentValues failed\n"); 38 | goto final; 39 | } 40 | 41 | for (int i = 0; i < count; ++i) { 42 | nsDbgPrint("%s res current %d\n", names[i], (int)values[i]); 43 | } 44 | 45 | if ((res = svcGetResourceLimitLimitValues(values, resLim, types, count))) { 46 | nsDbgPrint("svcGetResourceLimitLimitValues failed\n"); 47 | goto final; 48 | } 49 | 50 | for (int i = 0; i < count; ++i) { 51 | nsDbgPrint("%s res limit %d\n", names[i], (int)values[i]); 52 | } 53 | 54 | final: 55 | svcCloseHandle(resLim); 56 | } 57 | -------------------------------------------------------------------------------- /source/nwm_misc/rp_res.h: -------------------------------------------------------------------------------- 1 | #ifndef RP_RES_H 2 | #define RP_RES_H 3 | 4 | void rp_svc_increase_limits(void); 5 | void rp_svc_print_limits(void); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /source/nwm_misc/rp_syn.c: -------------------------------------------------------------------------------- 1 | #include "rp_syn.h" 2 | #include "3ds/result.h" 3 | 4 | u16 rp_atomic_fetch_addb_wrap(u16 *p, u16 a, u16 factor) { 5 | u16 v, v_new; 6 | do { 7 | v = __atomic_load_n(p, __ATOMIC_ACQUIRE); 8 | v_new = (v + a) % factor; 9 | } while (!__atomic_compare_exchange_n(p, &v, v_new, 1, __ATOMIC_RELEASE, __ATOMIC_RELAXED)); 10 | return v; 11 | } 12 | 13 | int rp_syn_init1(struct rp_syn_comp_func_t *syn1, int init, void *base, u32 stride, int count, void **pos) { 14 | int res; 15 | if ((res = rp_sem_close(syn1->sem))) 16 | return res; 17 | if ((res = rp_sem_init(syn1->sem, init ? count : 0, count))) 18 | return res; 19 | if ((res = rp_lock_close(syn1->mutex))) 20 | return res; 21 | if ((res = rp_lock_init(syn1->mutex))) 22 | return res; 23 | 24 | syn1->pos_head = syn1->pos_tail = 0; 25 | syn1->count = count; 26 | syn1->pos = pos; 27 | 28 | for (int i = 0; i < count; ++i) { 29 | syn1->pos[i] = init ? ((u8 *)base) + i * stride : 0; 30 | } 31 | return 0; 32 | } 33 | 34 | int rp_syn_acq(struct rp_syn_comp_func_t *syn1, s64 timeout, void **pos) { 35 | int res; 36 | if ((res = rp_sem_wait(syn1->sem, timeout)) != 0) { 37 | if (R_DESCRIPTION(res) != RD_TIMEOUT) 38 | nsDbgPrint("rp_syn_acq wait sem error: %d %d %d %d\n", 39 | R_LEVEL(res), R_SUMMARY(res), R_MODULE(res), R_DESCRIPTION(res)); 40 | return res; 41 | } 42 | u16 pos_tail = syn1->pos_tail; 43 | *pos = syn1->pos[pos_tail]; 44 | syn1->pos[pos_tail] = 0; 45 | syn1->pos_tail = (pos_tail + 1) % syn1->count; 46 | if (!*pos) { 47 | nsDbgPrint("error rp_syn_acq at pos %d\n", pos_tail); 48 | return -1; 49 | } 50 | return 0; 51 | } 52 | 53 | int rp_syn_rel(struct rp_syn_comp_func_t *syn1, void *pos) { 54 | u16 pos_head = syn1->pos_head; 55 | syn1->pos[pos_head] = pos; 56 | syn1->pos_head = (pos_head + 1) % syn1->count; 57 | int res; 58 | if ((res = rp_sem_rel(syn1->sem, 1))) { 59 | nsDbgPrint("rp_syn_rel rel sem error"); 60 | } 61 | return res; 62 | } 63 | 64 | int rp_syn_acq1(struct rp_syn_comp_func_t *syn1, s64 timeout, void **pos) { 65 | int res; 66 | if ((res = rp_sem_wait(syn1->sem, timeout)) != 0) { 67 | if (R_DESCRIPTION(res) != RD_TIMEOUT) 68 | nsDbgPrint("rp_syn_acq wait sem error: %d %d %d %d\n", 69 | R_LEVEL(res), R_SUMMARY(res), R_MODULE(res), R_DESCRIPTION(res)); 70 | return res; 71 | } 72 | u16 pos_tail = rp_atomic_fetch_addb_wrap(&syn1->pos_tail, 1, syn1->count); 73 | *pos = syn1->pos[pos_tail]; 74 | syn1->pos[pos_tail] = 0; 75 | if (!*pos) { 76 | nsDbgPrint("error rp_syn_acq at pos %d\n", pos_tail); 77 | return -1; 78 | } 79 | return 0; 80 | } 81 | 82 | int rp_syn_rel1(struct rp_syn_comp_func_t *syn1, void *pos) { 83 | int res; 84 | if ((res = rp_lock_wait(syn1->mutex, NWM_THREAD_WAIT_NS))) { 85 | if (R_DESCRIPTION(res) != RD_TIMEOUT) 86 | nsDbgPrint("rp_syn_rel1 wait mutex error: %d %d %d %d\n", 87 | R_LEVEL(res), R_SUMMARY(res), R_MODULE(res), R_DESCRIPTION(res)); 88 | return res; 89 | } 90 | 91 | u16 pos_head = syn1->pos_head; 92 | syn1->pos[pos_head] = pos; 93 | syn1->pos_head = (pos_head + 1) % syn1->count; 94 | if ((res = rp_lock_rel(syn1->mutex))) { 95 | nsDbgPrint("rp_syn_rel1 rel mutex error"); 96 | return res; 97 | } 98 | if ((res = rp_sem_rel(syn1->sem, 1))) { 99 | nsDbgPrint("rp_syn_rel1 rel sem error"); 100 | return res; 101 | } 102 | return 0; 103 | } 104 | -------------------------------------------------------------------------------- /source/nwm_misc/rp_syn.h: -------------------------------------------------------------------------------- 1 | #ifndef RP_SYN_H 2 | #define RP_SYN_H 3 | 4 | #include "global.h" 5 | 6 | typedef Handle rp_lock_t; 7 | typedef Handle rp_sem_t; 8 | 9 | #define rp_lock_init(n) svcCreateMutex(&n, 0) 10 | #define rp_lock_wait_try(n) svcWaitSynchronization(n, 0) 11 | #define rp_lock_wait(n, to) svcWaitSynchronization(n, to) 12 | #define rp_lock_rel(n) svcReleaseMutex(n) 13 | #define rp_lock_close(n) ({ int _ret = 0; do { if (n) _ret = svcCloseHandle(n); } while (0); _ret; }) 14 | 15 | #define rp_sem_init(n, i, m) svcCreateSemaphore(&n, i, m) 16 | #define rp_sem_wait_try(n) svcWaitSynchronization(n, 0) 17 | #define rp_sem_wait(n, to) svcWaitSynchronization(n, to) 18 | #define rp_sem_rel(n, c) ({ int _ret = 0; do { s32 count; _ret = svcReleaseSemaphore(&count, n, c); } while (0); _ret; }) 19 | #define rp_sem_close(n) ({ int _ret = 0; do { if (n) _ret = svcCloseHandle(n); } while (0); _ret; }) 20 | 21 | struct rp_syn_comp_func_t { 22 | rp_sem_t sem; 23 | rp_lock_t mutex; 24 | u16 pos_head, pos_tail; 25 | u16 count; 26 | void **pos; 27 | }; 28 | 29 | int rp_syn_init1(struct rp_syn_comp_func_t *syn1, int init, void *base, u32 stride, int count, void **pos); 30 | int rp_syn_acq(struct rp_syn_comp_func_t *syn1, s64 timeout, void **pos); 31 | int rp_syn_rel(struct rp_syn_comp_func_t *syn1, void *pos); 32 | int rp_syn_acq1(struct rp_syn_comp_func_t *syn1, s64 timeout, void **pos); 33 | int rp_syn_rel1(struct rp_syn_comp_func_t *syn1, void *pos); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /source/nwm_rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nwm_rs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["staticlib"] 8 | 9 | [dependencies] 10 | libc = { version = "0.2.170", default-features = false } 11 | oorandom = "11.1.4" 12 | function_name = "0.3.0" 13 | # Modified to work with union types 14 | const-default = { path = "../../const_default", version = "1.0.0", features = [ 15 | "derive", 16 | "unstable", 17 | ] } 18 | 19 | [build-dependencies] 20 | bindgen = "0.69.5" 21 | 22 | [features] 23 | img_diff_dbg = [] 24 | -------------------------------------------------------------------------------- /source/nwm_rs/build.rs: -------------------------------------------------------------------------------- 1 | // Adapted from ctru-sys build.rs 2 | 3 | use bindgen::callbacks::ParseCallbacks; 4 | use bindgen::{Builder, RustTarget}; 5 | 6 | use std::collections::HashSet; 7 | use std::env; 8 | use std::path::{Path, PathBuf}; 9 | 10 | use std::process::{Command, Output, Stdio}; 11 | 12 | #[derive(Debug)] 13 | struct Callback { 14 | names: HashSet, 15 | union_names: HashSet, 16 | } 17 | 18 | impl Callback { 19 | fn new() -> Self { 20 | let mut names = HashSet::::new(); 21 | names.insert("rp_cb".into()); 22 | 23 | let mut union_names = HashSet::::new(); 24 | union_names.insert("nwm_cb".into()); 25 | 26 | Self { names, union_names } 27 | } 28 | } 29 | 30 | impl ParseCallbacks for Callback { 31 | fn add_derives(&self, info: &bindgen::callbacks::DeriveInfo<'_>) -> Vec { 32 | if self.names.contains(info.name) { 33 | vec!["ConstDefault".into()] 34 | } else if self.union_names.contains(info.name) { 35 | vec!["ConstDefault".into()] 36 | } else { 37 | vec![] 38 | } 39 | } 40 | } 41 | 42 | fn main() { 43 | let devkitarm = env::var("DEVKITARM").unwrap(); 44 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); 45 | 46 | println!("cargo:rerun-if-changed=build.rs"); 47 | println!("cargo:rerun-if-env-changed=DEVKITPRO"); 48 | 49 | let gcc_version = get_gcc_version(PathBuf::from(&devkitarm).join("bin/arm-none-eabi-gcc")); 50 | let include_path_str = "../../include"; 51 | let include_path = Path::new(include_path_str); 52 | let ctru_include_path_str = "../../libctru/libctru/include"; 53 | let ctru_include_path = Path::new(ctru_include_path_str); 54 | let nwm_header_str = "nwm_rs.h"; 55 | let nwm_header = Path::new(nwm_header_str); 56 | 57 | let current_dir = env::current_dir().unwrap(); 58 | let rerun_headers = |path: &str| { 59 | let stdout = match Command::new("find") 60 | .args([path, "-name", "*.h", "-type", "f"]) 61 | .stderr(Stdio::inherit()) 62 | .output() 63 | { 64 | Ok(Output { stdout, status, .. }) if status.success() => stdout, 65 | Ok(Output { status, .. }) => { 66 | println!("cargo::error=find failed with status {status}"); 67 | return; 68 | } 69 | Err(err) => { 70 | println!("cargo::error=find failed {err}"); 71 | return; 72 | } 73 | }; 74 | for line in String::from_utf8_lossy(&stdout).lines() { 75 | let file = current_dir.join(line).canonicalize().unwrap(); 76 | let file = file.display(); 77 | println!("cargo:rerun-if-changed={file}"); 78 | } 79 | }; 80 | rerun_headers(".."); 81 | rerun_headers(include_path_str); 82 | rerun_headers(ctru_include_path_str); 83 | 84 | let sysroot = Path::new(&devkitarm).join("arm-none-eabi"); 85 | let system_include = sysroot.join("include"); 86 | let gcc_include = PathBuf::from(format!( 87 | "{devkitarm}/lib/gcc/arm-none-eabi/{gcc_version}/include" 88 | )); 89 | 90 | let bindings = Builder::default() 91 | .header(nwm_header.to_str().unwrap()) 92 | .rust_target(RustTarget::Nightly) 93 | .use_core() 94 | .trust_clang_mangling(false) 95 | .must_use_type("Result") 96 | .layout_tests(false) 97 | .ctypes_prefix("::libc") 98 | .prepend_enum_name(false) 99 | .blocklist_type("u(8|16|32|64)") 100 | .blocklist_type("__builtin_va_list") 101 | .blocklist_type("__va_list") 102 | .blocklist_function("handlePortCmd") 103 | .blocklist_function("setjmp") 104 | .blocklist_function("longjmp") 105 | .blocklist_var("nsConfig") 106 | .blocklist_var("ntrConfig") 107 | .blocklist_var("rpConfig") 108 | .opaque_type("MiiData") 109 | .derive_default(true) 110 | .clang_args([ 111 | "--target=arm-none-eabi", 112 | "--sysroot", 113 | sysroot.to_str().unwrap(), 114 | "-isystem", 115 | system_include.to_str().unwrap(), 116 | "-isystem", 117 | gcc_include.to_str().unwrap(), 118 | "-I", 119 | include_path.to_str().unwrap(), 120 | "-I", 121 | ctru_include_path.to_str().unwrap(), 122 | "-mfloat-abi=hard", 123 | "-march=armv6k", 124 | "-mtune=mpcore", 125 | "-mfpu=vfp", 126 | "-mtp=soft", 127 | "-DARM11", 128 | "-D__3DS__", 129 | "-fshort-enums", 130 | ]) 131 | .parse_callbacks(Box::new(Callback::new())) 132 | .generate() 133 | .expect("unable to generate bindings"); 134 | 135 | bindings 136 | .write_to_file(out_dir.join("bindings.rs")) 137 | .expect("Couldn't write bindings!"); 138 | } 139 | 140 | fn get_gcc_version(path_to_gcc: PathBuf) -> String { 141 | let Output { stdout, .. } = Command::new(path_to_gcc) 142 | .arg("--version") 143 | .stderr(Stdio::inherit()) 144 | .output() 145 | .unwrap(); 146 | 147 | let stdout_str = String::from_utf8_lossy(&stdout); 148 | 149 | stdout_str 150 | .split(|c: char| c.is_whitespace()) 151 | .nth(4) 152 | .unwrap() 153 | .to_string() 154 | } 155 | -------------------------------------------------------------------------------- /source/nwm_rs/nwm_rs.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "global.h" 3 | 4 | void __system_initSyscalls(void); 5 | #include "3ds/services/gspgpu.h" 6 | #include "3ds/result.h" 7 | #include "../nwm_misc/ikcp.h" 8 | #include "../nwm_misc/fecal.h" 9 | #include "../nwm_misc/gf256.h" 10 | #include "../nwm_misc/rp_syn.h" 11 | #include "../nwm_misc/rp_res.h" 12 | 13 | #define RP_RECV_BUF_N (2) 14 | struct rp_cb { 15 | struct IKCPCB ikcp; 16 | char send_bufs[SEND_BUFS_COUNT][NWM_PACKET_SIZE] ALIGNED(sizeof(void *)); 17 | char recv_buf[RP_RECV_BUF_N][RP_RECV_PACKET_SIZE] ALIGNED(sizeof(void *)); 18 | mp_pool_t send_pool; 19 | char cur_send_bufs[SEND_CUR_BUFS_COUNT][NWM_PACKET_SIZE] ALIGNED(sizeof(void *)); 20 | mp_pool_t cur_send_pool; 21 | struct rp_syn_comp_func_t nwm_syn; 22 | void *nwm_syn_data[SEND_BUFS_COUNT]; 23 | }; 24 | 25 | #define NWM_PROPORTIONAL_MIN_INTERVAL 1 26 | #define NWM_AGGRESSIVE_NEXT_TICK 1 27 | 28 | #include 29 | -------------------------------------------------------------------------------- /source/nwm_rs/src/dbg.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | // From c_str_macro crate 4 | macro_rules! c_str { 5 | ($lit:expr) => { 6 | (concat!($lit, "\0").as_ptr() as *const c_char) 7 | }; 8 | } 9 | 10 | macro_rules! nsDbgPrint { 11 | ($fn:ident $(, $es:expr)*) => { 12 | nsDbgPrint_t::$fn( 13 | c_str!(module_path!()), 14 | line!() as c_int, 15 | c_str!(function_name!()) 16 | $(, $es)* 17 | ) 18 | }; 19 | } 20 | 21 | macro_rules! nsDbgPrint_fn { 22 | ($fn:ident, $fmt:expr $(, $vn:ident: $ty:ty)*) => { 23 | pub fn $fn( 24 | file_name: *const c_char, 25 | line_number: c_int, 26 | func_name: *const c_char 27 | $(, $vn: $ty)* 28 | ) { 29 | unsafe { 30 | nsDbgPrintVerbose( 31 | file_name, 32 | line_number, 33 | func_name, 34 | c_str!($fmt) 35 | $(, $vn)* 36 | ); 37 | } 38 | } 39 | }; 40 | } 41 | 42 | pub struct nsDbgPrint_t { 43 | _z: (), 44 | } 45 | 46 | #[allow(dead_code)] 47 | impl nsDbgPrint_t { 48 | nsDbgPrint_fn!(trace, "Tracing...\n"); 49 | 50 | nsDbgPrint_fn!(int, "%s: %d\n", name: *const c_char, num: s32); 51 | 52 | nsDbgPrint_fn!(convSampPos, "xpos: %d, ypos: %d\n", xpos: s32, ypos: s32); 53 | 54 | nsDbgPrint_fn!(expBits, "num: %d, exp: %d\n", num: s32, exp: s32); 55 | 56 | nsDbgPrint_fn!(deltaProgQ, "Delta prog q: %d, projected size: %d, desired size: %d, comp count: %d\n", q: s32, projected_size: s32, desired_size: s32, comp_n: s32); 57 | 58 | nsDbgPrint_fn!(memUsage, "Mem usage: %08x\n", size: u32_); 59 | 60 | nsDbgPrint_fn!(mainLoopExit, "Nwm main loop exited\n"); 61 | 62 | nsDbgPrint_fn!(mainLoopReset, "Nwm main loop restarted\n"); 63 | 64 | nsDbgPrint_fn!(singalPortFailed, "Signal port event failed: %08x\n", ret: s32); 65 | 66 | nsDbgPrint_fn!(openProcessFailed, "Open process failed: %08x\n", ret: s32); 67 | 68 | nsDbgPrint_fn!(copyRemoteMemoryFailed, "Copy remote memory failed: %08x\n", ret: s32); 69 | 70 | nsDbgPrint_fn!(gspInitFailed, "GSP init failed: %08x\n", ret: s32); 71 | 72 | nsDbgPrint_fn!(fecalInitFailed, "FEC-AL init failed\n"); 73 | 74 | nsDbgPrint_fn!(initJpegFailed, "JPEG init failed\n"); 75 | 76 | nsDbgPrint_fn!(createEventFailed, "Create %s event failed %08x\n", name: *const c_char, ret: s32); 77 | 78 | nsDbgPrint_fn!(createRestartEventFailed, "Create restart event failed %08x\n", ret: s32); 79 | 80 | nsDbgPrint_fn!(signalRestartEventFailed, "Signal restart event failed %08x\n", ret: s32); 81 | 82 | nsDbgPrint_fn!(waitRestartEventFailed, "Wait restart event failed %08x\n", ret: s32); 83 | 84 | nsDbgPrint_fn!(createPortEventFailed, "Create port event failed %08x\n", ret: s32); 85 | 86 | nsDbgPrint_fn!(createNwmEventFailed, "Create nwm event failed %08x\n", ret: s32); 87 | 88 | nsDbgPrint_fn!(createNwmMutexFailed, "Create nwm mutex failed %08x\n", ret: s32); 89 | 90 | nsDbgPrint_fn!(createNwmRecvEventFailed, "Create nwm recv event failed %08x\n", ret: s32); 91 | 92 | nsDbgPrint_fn!(createNwmSvcFailed, "Create remote play service thread failed: %08x\n", ret: s32); 93 | 94 | nsDbgPrint_fn!(setThreadPriorityFailed, "Set thread priority failed: %08x\n", res: s32); 95 | 96 | nsDbgPrint_fn!(createMutexFailed, "Create %s mutex failed %08x\n", name: *const c_char, ret: s32); 97 | 98 | nsDbgPrint_fn!(createSemaphoreFailed, "Create %s semaphore failed: %08x\n", name: *const c_char, res: s32); 99 | 100 | nsDbgPrint_fn!(releaseSemaphoreFailed, "Release %s semaphore failed (%x): %08x\n", name: *const c_char, w: u32_, res: s32); 101 | 102 | nsDbgPrint_fn!(releaseMutexFailed, "Release %s mutex failed: %08x\n", name: *const c_char, res: s32); 103 | 104 | nsDbgPrint_fn!(allocFailed, "Bad alloc, size: %x/%x\n", total_size: u32_, alloc_size: u32_); 105 | 106 | nsDbgPrint_fn!(sendBufferOverflow, "Send buffer overflow\n"); 107 | 108 | nsDbgPrint_fn!(waitNwmEventSyncFailed, "Sync wait nwm event failed %08x\n", ret: s32); 109 | 110 | nsDbgPrint_fn!(waitNwmEventClearFailed, "Clear wait nwm event failed %08x\n", ret: s32); 111 | 112 | nsDbgPrint_fn!(waitNwmEventSignalFailed, "Signal wait nwm event failed %08x\n", ret: s32); 113 | 114 | nsDbgPrint_fn!(nwmEventSignalFailed, "Signal nwm event failed %08x\n", ret: s32); 115 | 116 | nsDbgPrint_fn!(waitForSyncFailed, "Wait for %s sync failed: %08x\n", name: *const c_char, res: s32); 117 | 118 | nsDbgPrint_fn!(encodeMcuFailed, "Encode MCUs failed, restarting...\n"); 119 | 120 | nsDbgPrint_fn!(nwmOutputOverflow, "Nwm output packet len overflow: %08x\n", len: s32); 121 | 122 | nsDbgPrint_fn!(nwmInputNothing, "Nwm input nothing\n"); 123 | 124 | nsDbgPrint_fn!(nwmInputFailed, "Nwm input failed: %08x, errno = %08x\n", ret: s32, errno: s32); 125 | 126 | nsDbgPrint_fn!(kcpInputFailed, "KCP input failed: %08x\n", ret: s32); 127 | 128 | nsDbgPrint_fn!(kcpSendFailed, "KCP send failed: %08x\n", ret: s32); 129 | 130 | nsDbgPrint_fn!(kcpFlushFailed, "KCP flush failed: %08x\n", ret: s32); 131 | 132 | nsDbgPrint_fn!(kcpTimeout, "KCP timeout\n"); 133 | 134 | nsDbgPrint_fn!(mpInitFailed, "Mem pool %s init failed\n", name: *const c_char); 135 | 136 | nsDbgPrint_fn!(rpSynInitFailed, "Nwm syn init failed\n"); 137 | 138 | nsDbgPrint_fn!(mpAllocFailed, "Mem pool %s alloc failed\n", name: *const c_char); 139 | 140 | nsDbgPrint_fn!(mpFreeFailed, "Mem pool %s free failed\n", name: *const c_char); 141 | } 142 | -------------------------------------------------------------------------------- /source/nwm_rs/src/entries.rs: -------------------------------------------------------------------------------- 1 | mod handle_port; 2 | mod start_up; 3 | mod thread_aux; 4 | mod thread_main; 5 | mod thread_nwm; 6 | mod thread_screen; 7 | mod work_thread; 8 | 9 | pub use thread_nwm::{ 10 | get_min_send_interval_ns, get_min_send_interval_tick, get_next_send_tick, 11 | get_reliable_stream_delta_prog, get_reliable_stream_method, nwm_is_waiting, 12 | packet_data_size_kcp, rp_delta_q_qos, rp_dq_update_size, rp_send_buffer, NwmInfo, 13 | ReliableStreamMethod, 14 | }; 15 | // pub use thread_screen::{get_no_skip_frame, FRAME_TIMING_FACTOR_DQ}; 16 | pub use work_thread::{get_frame_time, reset_threads, set_reset_threads_ar}; 17 | -------------------------------------------------------------------------------- /source/nwm_rs/src/entries/handle_port.rs: -------------------------------------------------------------------------------- 1 | mod safe_impl; 2 | mod vars; 3 | 4 | use crate::*; 5 | use vars::*; 6 | -------------------------------------------------------------------------------- /source/nwm_rs/src/entries/handle_port/safe_impl.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[named] 4 | pub fn handlePort(t: ThreadVars, cmd_id: u32_, norm_params: &[u32_], trans_params: &[u32_]) { 5 | match cmd_id as u8_ { 6 | SVC_NWM_CMD_OVERLAY_CALLBACK => { 7 | let is_top = *norm_params.get(0).unwrap_or(&u32_::MAX); 8 | 9 | let game_pid = *trans_params.get(1).unwrap_or(&0); 10 | if is_top > 1 { 11 | t.set_port_game_pid_ar(0); 12 | } else { 13 | if t.port_game_pid() != game_pid { 14 | t.set_port_game_pid_ar(game_pid); 15 | } 16 | let ret = t.signal_port_event(is_top > 0); 17 | if ret != 0 { 18 | nsDbgPrint!(singalPortFailed, ret); 19 | } 20 | } 21 | } 22 | SVC_NWM_CMD_PARAMS_UPDATE => { 23 | if t.config().set_ar(norm_params) { 24 | t.set_reset_threads_ar(); 25 | } 26 | } 27 | SVC_NWM_CMD_GAME_PID_UPDATE => { 28 | let game_pid = *norm_params.get(0).unwrap_or(&0); 29 | t.config().set_game_pid_ar(game_pid); 30 | } 31 | _ => (), 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /source/nwm_rs/src/entries/handle_port/vars.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub struct Config(()); 4 | 5 | impl Config { 6 | pub fn set_game_pid_ar(&self, v: u32_) { 7 | unsafe { AtomicU32::from_mut(&mut (*rp_config).gamePid) }.store(v, Ordering::Relaxed); 8 | } 9 | 10 | pub fn set_ar(&self, a: &[u32_]) -> bool { 11 | if a.len() >= rp_config_u32_count { 12 | for i in 0..rp_config_u32_count { 13 | let f = unsafe { AtomicU32::from_ptr((rp_config as *mut u32_).add(i)) }; 14 | let p = unsafe { *a.as_ptr().add(i) }; 15 | f.store(p, Ordering::Relaxed); 16 | } 17 | true 18 | } else { 19 | false 20 | } 21 | } 22 | } 23 | 24 | pub struct ThreadVars(()); 25 | 26 | impl ThreadVars { 27 | pub fn config(&self) -> Config { 28 | Config(()) 29 | } 30 | 31 | pub fn set_port_game_pid_ar(&self, v: u32_) { 32 | unsafe { 33 | crate::entries::thread_screen::set_port_game_pid(v); 34 | // self.config() 35 | // .set_game_pid_ar(if v == (*ntr_config).HomeMenuPid { 0 } else { v }); 36 | } 37 | } 38 | 39 | pub fn port_game_pid(&self) -> u32_ { 40 | unsafe { crate::entries::thread_screen::get_port_game_pid() } 41 | } 42 | 43 | pub fn set_reset_threads_ar(&self) { 44 | crate::entries::work_thread::set_reset_threads_ar() 45 | } 46 | 47 | pub fn signal_port_event(&self, is_top: bool) -> Result { 48 | unsafe { 49 | svcSignalEvent( 50 | *(*syn_handles) 51 | .port_screen_ready 52 | .get(&ScreenIndex::from_bool(is_top)), 53 | ) 54 | } 55 | } 56 | } 57 | 58 | #[no_mangle] 59 | extern "C" fn handlePortCmd( 60 | cmd_id: u32_, 61 | norm_params_count: u32_, 62 | trans_params_size: u32_, 63 | cmd_buf1: *const u32_, 64 | ) { 65 | unsafe { 66 | safe_impl::handlePort( 67 | ThreadVars(()), 68 | cmd_id, 69 | slice::from_raw_parts(cmd_buf1, norm_params_count as usize), 70 | slice::from_raw_parts( 71 | cmd_buf1.add(norm_params_count as usize), 72 | trans_params_size as usize, 73 | ), 74 | ) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /source/nwm_rs/src/entries/start_up.rs: -------------------------------------------------------------------------------- 1 | mod safe_impl; 2 | mod vars; 3 | 4 | use crate::*; 5 | use vars::*; 6 | -------------------------------------------------------------------------------- /source/nwm_rs/src/entries/start_up/safe_impl.rs: -------------------------------------------------------------------------------- 1 | use super::vars::NwmHdr; 2 | use super::*; 3 | 4 | pub fn start_up(t: ThreadVars, nwm_hdr: NwmHdr) { 5 | let protocol = nwm_hdr.protocol(); 6 | let src_port = nwm_hdr.src_port(); 7 | let dst_port = nwm_hdr.dst_port(); 8 | 9 | let tcp_hit = protocol == 0x6 && src_port == utils::htons(NS_MENU_LISTEN_PORT as u16); 10 | let udp_hit = protocol == 0x11 11 | && src_port == utils::htons(NWM_INIT_SRC_PORT as u16) 12 | && dst_port == utils::htons(NWM_INIT_DST_PORT as u16); 13 | 14 | if tcp_hit || udp_hit { 15 | let saddr = nwm_hdr.srcAddr(); 16 | let daddr = nwm_hdr.dstAddr(); 17 | 18 | if t.inited() { 19 | let mut need_update = false; 20 | let rp_daddr = t.config().dst_addr_ar(); 21 | if (tcp_hit && rp_daddr == 0) || udp_hit { 22 | if rp_daddr != daddr { 23 | t.config().set_dst_addr_ar_and_update(daddr); 24 | 25 | need_update = true; 26 | } 27 | } 28 | if t.src_addr() != saddr { 29 | t.set_src_addr(saddr); 30 | 31 | need_update = true; 32 | } 33 | 34 | if need_update { 35 | t.set_nwm_hdr(&nwm_hdr); 36 | } 37 | return; 38 | } 39 | 40 | if !t.open_home_process() { 41 | return; 42 | } 43 | 44 | t.set_inited(); 45 | 46 | t.set_nwm_hdr(&nwm_hdr); 47 | t.config().set_dst_addr_ar_and_update(daddr); 48 | t.set_src_addr(saddr); 49 | 50 | create_thread_from_pool::<{ RP_THREAD_STACK_SIZE as usize }>( 51 | t.thread_main_handle(), 52 | Some(crate::entries::thread_main::encode_thread_main), 53 | 0, 54 | RP_THREAD_PRIO_DEFAULT as s32, 55 | 2, 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /source/nwm_rs/src/entries/start_up/vars.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | pub struct NwmHdr<'a>(&'a [u8_; Self::N]); 3 | 4 | impl NwmHdr<'_> { 5 | const N: usize = NWM_HDR_SIZE as usize; 6 | } 7 | 8 | static mut rp_inited: bool = false; 9 | static mut rp_src_addr: u32_ = 0; 10 | 11 | pub struct ThreadVars(()); 12 | 13 | pub struct Config(()); 14 | 15 | impl Config { 16 | fn dst_addr_ar_wrap(&self) -> &AtomicU32 { 17 | unsafe { AtomicU32::from_mut(&mut (*rp_config).dstAddr) } 18 | } 19 | 20 | pub fn dst_addr_ar(&self) -> u32_ { 21 | self.dst_addr_ar_wrap().load(Ordering::Relaxed) 22 | } 23 | 24 | fn set_dst_addr_ar(&self, v: u32_) { 25 | self.dst_addr_ar_wrap().store(v, Ordering::Relaxed) 26 | } 27 | 28 | #[named] 29 | pub fn set_dst_addr_ar_and_update(&self, mut v: u32_) { 30 | self.set_dst_addr_ar(v); 31 | 32 | let res = unsafe { 33 | copyRemoteMemory( 34 | home_process_handle, 35 | ptr::addr_of_mut!((*rp_config).dstAddr) as *mut c_void, 36 | CUR_PROCESS_HANDLE, 37 | ptr::addr_of_mut!(v) as *mut c_void, 38 | mem::size_of::() as u32_, 39 | ) as i32 40 | }; 41 | if res != 0 { 42 | nsDbgPrint!(copyRemoteMemoryFailed, res); 43 | } 44 | } 45 | } 46 | 47 | impl ThreadVars { 48 | pub fn config(&self) -> Config { 49 | Config(()) 50 | } 51 | 52 | pub fn thread_main_handle(&self) -> *mut Handle { 53 | ptr::addr_of_mut!(thread_main_handle) 54 | } 55 | 56 | #[named] 57 | pub fn open_home_process(&self) -> bool { 58 | unsafe { 59 | let res = svcOpenProcess(&mut home_process_handle, (*ntr_config).HomeMenuPid); 60 | if res != 0 { 61 | nsDbgPrint!(openProcessFailed, res); 62 | false 63 | } else { 64 | true 65 | } 66 | } 67 | } 68 | 69 | pub fn inited(&self) -> bool { 70 | unsafe { rp_inited } 71 | } 72 | 73 | pub fn set_inited(&self) { 74 | unsafe { rp_inited = true } 75 | } 76 | 77 | pub fn src_addr(&self) -> u32_ { 78 | unsafe { rp_src_addr } 79 | } 80 | 81 | pub fn set_src_addr(&self, v: u32_) { 82 | unsafe { rp_src_addr = v } 83 | } 84 | 85 | pub fn set_nwm_hdr(&self, v: &NwmHdr) { 86 | unsafe { 87 | ptr::copy_nonoverlapping( 88 | v.0.as_ptr(), 89 | crate::entries::thread_nwm::get_current_nwm_hdr().as_mut_ptr(), 90 | NwmHdr::N, 91 | ) 92 | } 93 | } 94 | } 95 | 96 | impl NwmHdr<'_> { 97 | pub fn protocol(&self) -> u8_ { 98 | self.0[0x17 + 0x8] 99 | } 100 | 101 | pub fn src_port(&self) -> u16_ { 102 | unsafe { *(&self.0[0x22 + 0x8] as *const u8_ as *const u16_) } 103 | } 104 | 105 | pub fn dst_port(&self) -> u16_ { 106 | unsafe { *(&self.0[0x22 + 0xa] as *const u8_ as *const u16_) } 107 | } 108 | 109 | pub fn srcAddr(&self) -> u32_ { 110 | unsafe { (&self.0[0x1a + 0x8] as *const u8_ as *const u32_).read_unaligned() } 111 | } 112 | 113 | pub fn dstAddr(&self) -> u32_ { 114 | unsafe { (&self.0[0x1e + 0x8] as *const u8_ as *const u32_).read_unaligned() } 115 | } 116 | } 117 | 118 | #[no_mangle] 119 | extern "C" fn rpStartup(buf: *const u8_) { 120 | let buf = unsafe { mem::transmute(buf) }; 121 | let buf = NwmHdr(buf); 122 | safe_impl::start_up(ThreadVars(()), buf) 123 | } 124 | -------------------------------------------------------------------------------- /source/nwm_rs/src/entries/thread_aux.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | pub extern "C" fn thread_aux(p: *mut c_void) { 4 | unsafe { 5 | __system_initSyscalls(); 6 | let t = crate::ThreadId::init_unchecked(p as u32_); 7 | crate::entries::work_thread::work_thread_loop(t); 8 | crate::entries::work_thread::set_reset_threads_ar(); 9 | svcExitThread() 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /source/nwm_rs/src/entries/thread_nwm.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | mod safe_impl; 4 | mod vars; 5 | pub use vars::*; 6 | -------------------------------------------------------------------------------- /source/nwm_rs/src/entries/thread_nwm/safe_impl.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub fn try_send_next_buffer(v: ThreadVars, work_flush: bool) -> bool { 4 | try_send_next_buffer_may_skip(v, work_flush, false) 5 | } 6 | 7 | fn try_send_next_buffer_may_skip(v: ThreadVars, work_flush: bool, mut may_skip: bool) -> bool { 8 | let work_index = *v.work_index(); 9 | let mut thread_id = *v.thread_id(); 10 | 11 | loop { 12 | if !v.sync(work_flush) { 13 | return false; 14 | } 15 | 16 | let ninfo = v.nwm_info(); 17 | let dinfo = &ninfo.info; 18 | 19 | let (filled, pos, flag) = data_buf_filled(dinfo); 20 | if !filled { 21 | return may_skip; 22 | } 23 | 24 | if !send_next_buffer_delay(&v, work_flush, pos, flag) { 25 | return false; 26 | } 27 | 28 | if work_flush { 29 | if work_index != *v.work_index() { 30 | return true; 31 | } 32 | 33 | if thread_id != *v.thread_id() { 34 | thread_id = *v.thread_id(); 35 | } 36 | 37 | may_skip = false; 38 | continue; 39 | } 40 | 41 | return true; 42 | } 43 | } 44 | 45 | fn send_next_buffer_delay(v: &ThreadVars, work_flush: bool, pos: *mut u8, flag: u32) -> bool { 46 | unsafe { 47 | let curr_tick = svcGetSystemTick() as u32_; 48 | let next_tick = v.next_send_tick().load(Ordering::Relaxed); 49 | let tick_diff = next_tick as s32 - curr_tick as s32; 50 | 51 | if tick_diff > 0 { 52 | if work_flush { 53 | let sleep_value = tick_diff as u64_ * 1000_000_000 / SYSCLOCK_ARM11 as u64_; 54 | svcSleepThread(sleep_value as s64); 55 | if !send_next_buffer( 56 | &v, 57 | if NWM_AGGRESSIVE_NEXT_TICK > 0 { 58 | next_tick 59 | } else { 60 | svcGetSystemTick() as u32_ 61 | }, 62 | pos, 63 | flag, 64 | ) { 65 | return false; 66 | } 67 | } 68 | } else { 69 | if !send_next_buffer(&v, curr_tick, pos, flag) { 70 | return false; 71 | } 72 | } 73 | true 74 | } 75 | } 76 | 77 | unsafe fn send_next_buffer(v: &ThreadVars, tick: u32_, pos: *mut u8_, flag: u32_) -> bool { 78 | let thread_id = *v.thread_id(); 79 | let core_count = crate::entries::work_thread::get_core_count_in_use(); 80 | 81 | let winfo = v.nwm_infos(); 82 | let ninfo = winfo.get_mut(&thread_id); 83 | let dinfo = &ninfo.info; 84 | 85 | let send_pos = dinfo.send_pos; 86 | let data_buf = send_pos; 87 | let packet_buf = data_buf.sub(DATA_HDR_SIZE as usize); 88 | 89 | let size = pos.offset_from(send_pos) as u32_; 90 | let packet_data_size = get_packet_data_size() as u32_; 91 | let size = cmp::min(size, packet_data_size); 92 | 93 | let thread_emptied = send_pos.add(size as usize) == pos; 94 | let thread_done = thread_emptied && flag > 0; 95 | 96 | if size < packet_data_size && !thread_done { 97 | return false; 98 | } 99 | 100 | let mut thread_end_id = thread_id; 101 | let mut end_size = size; 102 | 103 | let mut thread_end_done = thread_done; 104 | 105 | if thread_done { 106 | thread_end_id.next_wrapped_n(&core_count); 107 | } 108 | 109 | let mut total_size = size; 110 | 111 | if thread_done && thread_end_id.get() != 0 { 112 | loop { 113 | let ninfo = winfo.get_mut(&thread_end_id); 114 | let dinfo = &ninfo.info; 115 | 116 | let (filled, pos, flag) = data_buf_filled(dinfo); 117 | if !filled { 118 | return false; 119 | } 120 | 121 | let data_buf_end = data_buf.add(total_size as usize); 122 | 123 | let send_pos = dinfo.send_pos; 124 | 125 | let remaining_size = packet_data_size - total_size; 126 | 127 | let size = pos.offset_from(send_pos) as u32_; 128 | let size = cmp::min(size, remaining_size); 129 | 130 | let thread_emptied = send_pos.add(size as usize) == pos; 131 | let thread_done = thread_emptied && flag > 0; 132 | 133 | if size < remaining_size && !thread_done { 134 | return false; 135 | } 136 | 137 | ptr::copy_nonoverlapping(send_pos, data_buf_end, size as usize); 138 | total_size += size; 139 | 140 | end_size = size; 141 | 142 | thread_end_done = thread_done; 143 | if thread_done { 144 | thread_end_id.next_wrapped_n(&core_count); 145 | if thread_end_id.get() == 0 { 146 | break; 147 | } 148 | continue; 149 | } 150 | break; 151 | } 152 | } 153 | 154 | let data_buf_hdr = v.data_buf_hdr(); 155 | ptr::copy( 156 | data_buf_hdr.as_ptr(), 157 | packet_buf as *mut u8_, 158 | DATA_HDR_SIZE as usize, 159 | ); 160 | if thread_end_done && thread_end_id.get() == 0 { 161 | *packet_buf.add(1) |= flag as u8_; 162 | } 163 | *data_buf_hdr.get_unchecked_mut(3) += 1; 164 | 165 | let packet_size = total_size + DATA_HDR_SIZE; 166 | if rp_output(packet_buf, packet_size as usize) == None { 167 | return false; 168 | } 169 | v.next_send_tick().store( 170 | tick + if NWM_PROPORTIONAL_MIN_INTERVAL > 0 { 171 | v.min_send_interval_tick() * packet_size / PACKET_SIZE 172 | } else { 173 | v.min_send_interval_tick() 174 | }, 175 | Ordering::Relaxed, 176 | ); 177 | 178 | if !thread_end_done { 179 | let send_pos = &mut winfo.get_mut(&thread_end_id).info.send_pos; 180 | *send_pos = (*send_pos).add(end_size as usize); 181 | } 182 | 183 | if thread_done { 184 | *v.thread_id() = thread_end_id; 185 | 186 | if v.thread_id().get() == 0 { 187 | v.release(); 188 | } 189 | } 190 | 191 | true 192 | } 193 | 194 | fn data_buf_filled(dinfo: &DataBufInfo) -> (bool, *mut u8_, u32_) { 195 | let flag = dinfo.flag.load(Ordering::Acquire); 196 | let pos = dinfo.pos.load(Ordering::Acquire); 197 | (dinfo.send_pos < pos || flag > 0, pos, flag) 198 | } 199 | -------------------------------------------------------------------------------- /source/nwm_rs/src/entries/thread_screen.rs: -------------------------------------------------------------------------------- 1 | mod safe_impl; 2 | mod vars; 3 | 4 | use crate::*; 5 | pub use vars::*; 6 | -------------------------------------------------------------------------------- /source/nwm_rs/src/entries/work_thread.rs: -------------------------------------------------------------------------------- 1 | mod safe_impl; 2 | mod vars; 3 | 4 | use crate::*; 5 | pub use vars::*; 6 | -------------------------------------------------------------------------------- /source/nwm_rs/src/fix.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | pub const SCALE_BITS: u32_ = 16; 4 | 5 | pub const ONE_HALF: u32_ = 1 << (SCALE_BITS - 1); 6 | 7 | pub const fn FIX(x: c_double) -> u32_ { 8 | (x * (1 << SCALE_BITS) as c_double + 0.5) as u32_ 9 | } 10 | 11 | #[derive(Copy, Clone, ConstDefault, PartialEq, Eq, PartialOrd, Ord)] 12 | pub struct Fix(pub u64_); 13 | 14 | #[derive(Copy, Clone, ConstDefault, PartialEq, Eq, PartialOrd, Ord)] 15 | pub struct Fix32(pub u32_); 16 | 17 | #[allow(dead_code)] 18 | impl Fix { 19 | pub const fn fix(v: u32_) -> Self { 20 | Self((v as u64_) << SCALE_BITS) 21 | } 22 | 23 | pub const fn unfix(&self) -> u32_ { 24 | ((self.0 + ONE_HALF as u64_) >> SCALE_BITS) as u32_ 25 | } 26 | 27 | pub const fn store_to_u32(&self) -> Fix32 { 28 | Fix32(self.0 as u32_) 29 | } 30 | 31 | pub const fn fix32(v: u32_) -> Fix32 { 32 | Self::fix(v).store_to_u32() 33 | } 34 | 35 | pub const fn load_from_u32(v: Fix32) -> Self { 36 | Self(v.0 as u64_) 37 | } 38 | } 39 | 40 | impl Add for Fix { 41 | type Output = Self; 42 | 43 | fn add(self, rhs: Self) -> Self::Output { 44 | Self(self.0 + rhs.0) 45 | } 46 | } 47 | 48 | impl Sub for Fix { 49 | type Output = Self; 50 | 51 | fn sub(self, rhs: Self) -> Self::Output { 52 | Self(self.0 - rhs.0) 53 | } 54 | } 55 | 56 | impl Mul for Fix { 57 | type Output = Self; 58 | 59 | fn mul(self, rhs: Self) -> Self::Output { 60 | Self((self.0 * rhs.0) >> SCALE_BITS) 61 | } 62 | } 63 | 64 | impl Div for Fix { 65 | type Output = Self; 66 | 67 | fn div(self, rhs: Self) -> Self::Output { 68 | // now we have a panic handler, however abort when divide-by-zero isn't much worse 69 | unsafe { Self(core::intrinsics::unchecked_div(self.0 << SCALE_BITS, rhs.0)) } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /source/nwm_rs/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![allow(non_upper_case_globals)] 3 | #![allow(non_camel_case_types)] 4 | #![allow(non_snake_case)] 5 | #![allow(internal_features)] 6 | #![allow(incomplete_features)] 7 | #![feature(atomic_from_mut)] 8 | #![feature(core_intrinsics)] 9 | #![feature(const_trait_impl)] 10 | #![feature(generic_const_exprs)] 11 | #![feature(generic_const_items)] 12 | #![feature(adt_const_params)] 13 | #![feature(inherent_associated_types)] 14 | #![feature(trivial_bounds)] 15 | #![feature(maybe_uninit_uninit_array)] 16 | #![feature(maybe_uninit_array_assume_init)] 17 | #![feature(stmt_expr_attributes)] 18 | #![feature(array_chunks)] 19 | #![feature(iter_array_chunks)] 20 | #![feature(ptr_sub_ptr)] 21 | #![feature(array_ptr_get)] 22 | #![feature(generic_arg_infer)] 23 | #![feature(get_many_mut)] 24 | #![feature(more_float_constants)] 25 | #![allow(static_mut_refs)] 26 | 27 | use crate::dbg::*; 28 | use crate::fix::*; 29 | use crate::utils::*; 30 | use crate::vars::*; 31 | use ::libc::*; 32 | use const_default::{const_default, ConstDefault}; 33 | use core::ops::*; 34 | use core::panic::PanicInfo; 35 | use core::sync::atomic::*; 36 | use core::{ 37 | cmp, 38 | marker::{ConstParamTy, PhantomData}, 39 | mem, ptr, slice, 40 | }; 41 | use ctru::*; 42 | use function_name::named; 43 | use oorandom::Rand32; 44 | 45 | #[allow(unused)] 46 | mod ctru { 47 | use crate::ConstDefault; 48 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 49 | } 50 | 51 | #[macro_use] 52 | mod dbg; 53 | mod entries; 54 | mod fix; 55 | mod jpeg; 56 | mod utils; 57 | mod vars; 58 | 59 | #[panic_handler] 60 | fn panic(panic_info: &PanicInfo) -> ! { 61 | unsafe { 62 | if let Some(location) = panic_info.location() { 63 | panicHandle( 64 | location.file().as_ptr() as *mut _, 65 | location.file().len() as i32, 66 | location.line() as i32, 67 | location.column() as i32, 68 | ) 69 | } else { 70 | showMsgRaw(c_str!("Panic!")); 71 | } 72 | 73 | loop { 74 | svcSleepThread(THREAD_WAIT_NS); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /source/nwm_rs/src/utils.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | pub const fn htons(v: u16_) -> u16_ { 4 | v.to_be() 5 | } 6 | 7 | pub const fn ntohs(v: u16_) -> u16_ { 8 | v.swap_bytes() 9 | } 10 | 11 | pub const fn align_to_page_size(s: usize) -> usize { 12 | if s == 0 { 13 | 0 14 | } else { 15 | ((s - 1) / 0x1000 + 1) * 0x1000 16 | } 17 | } 18 | 19 | pub struct MemRegionBase(pub mem::MaybeUninit<[B; T]>); 20 | 21 | impl MemRegionBase { 22 | pub fn to_ptr(&mut self) -> *mut B { 23 | unsafe { mem::transmute(self) } 24 | } 25 | } 26 | 27 | pub type MemRegion8 = MemRegionBase; 28 | 29 | impl MemRegion8 { 30 | unsafe fn from_ptr<'a>(p: *mut u8_) -> &'a mut Self { 31 | mem::transmute(p) 32 | } 33 | } 34 | 35 | pub struct StackRegionCount; 36 | 37 | impl StackRegionCount { 38 | const N: usize = T / mem::size_of::(); 39 | } 40 | 41 | #[allow(unused)] 42 | pub struct StackRegionSize; 43 | 44 | #[allow(unused)] 45 | impl StackRegionSize { 46 | const T: usize = N * mem::size_of::(); 47 | } 48 | 49 | pub type StackRegion = MemRegionBase::N }>; 50 | 51 | pub fn stack_region_from_mem_region<'a, const T: usize>( 52 | m: &'a mut MemRegion8, 53 | ) -> &'a mut StackRegion 54 | where 55 | [(); StackRegionCount::::N]:, 56 | { 57 | unsafe { mem::transmute(m) } 58 | } 59 | 60 | pub fn request_mem_from_pool() -> Option<&'static mut MemRegion8> { 61 | let s = unsafe { plgRequestMemory(T as u32_) }; 62 | if s > 0 { 63 | let t = unsafe { MemRegion8::::from_ptr(s as *mut u8_) }; 64 | Some(t) 65 | } else { 66 | None 67 | } 68 | } 69 | 70 | #[allow(dead_code)] 71 | pub fn request_mem_from_pool_vsize(t: usize) -> Option<&'static mut [u8_]> { 72 | let s = unsafe { plgRequestMemory(t as u32_) }; 73 | if s > 0 { 74 | let t = unsafe { slice::from_raw_parts_mut(s as *mut u8_, t) }; 75 | Some(t) 76 | } else { 77 | None 78 | } 79 | } 80 | 81 | pub struct PhantomResult<'a>(pub Result, PhantomData<&'a ()>); 82 | 83 | pub fn create_thread<'a, 'b: 'a, const T: usize>( 84 | h: *mut Handle, 85 | f: ThreadFunc, 86 | a: u32_, 87 | t: &'b mut StackRegion, 88 | prio: s32, 89 | core: s32, 90 | ) -> PhantomResult<'a> 91 | where 92 | [(); StackRegionCount::::N]:, 93 | [(); T - SMALL_STACK_SIZE as usize]:, 94 | { 95 | unsafe { 96 | PhantomResult( 97 | svcCreateThread( 98 | h, 99 | f, 100 | a, 101 | t.to_ptr().add(StackRegionCount::::N - 10), 102 | prio, 103 | core, 104 | ), 105 | PhantomData, 106 | ) 107 | } 108 | } 109 | 110 | pub struct CreateThread<'a>(Handle, PhantomData<&'a ()>); 111 | 112 | impl<'a> CreateThread<'a> { 113 | pub fn create<'b: 'a, const T: usize>( 114 | f: ThreadFunc, 115 | a: u32_, 116 | t: &'b mut StackRegion, 117 | prio: s32, 118 | core: s32, 119 | ) -> Option 120 | where 121 | [(); StackRegionCount::::N]:, 122 | [(); T - SMALL_STACK_SIZE as usize]:, 123 | { 124 | let mut h = mem::MaybeUninit::::uninit(); 125 | let res = create_thread(h.as_mut_ptr(), f, a, t, prio, core); 126 | if res.0 != 0 { 127 | None 128 | } else { 129 | let h = unsafe { h.assume_init() }; 130 | Some(Self(h, PhantomData)) 131 | } 132 | } 133 | } 134 | 135 | impl<'a> Drop for CreateThread<'a> { 136 | fn drop(&mut self) { 137 | unsafe { 138 | let _ = svcCloseHandle(self.0); 139 | } 140 | } 141 | } 142 | 143 | pub struct JoinThread<'a>(CreateThread<'a>); 144 | 145 | impl<'a> JoinThread<'a> { 146 | pub fn create(t: CreateThread<'a>) -> Self { 147 | Self(t) 148 | } 149 | } 150 | 151 | impl<'a> Drop for JoinThread<'a> { 152 | fn drop(&mut self) { 153 | unsafe { 154 | let _ = svcWaitSynchronization(self.0 .0, -1); 155 | } 156 | } 157 | } 158 | 159 | pub fn create_thread_from_pool<'a, const T: usize>( 160 | h: *mut Handle, 161 | f: ThreadFunc, 162 | a: u32_, 163 | prio: s32, 164 | core: s32, 165 | ) -> PhantomResult<'a> 166 | where 167 | [(); StackRegionCount::::N]:, 168 | [(); T - SMALL_STACK_SIZE as usize]:, 169 | { 170 | if let Some(t) = request_mem_from_pool::() { 171 | create_thread(h, f, a, stack_region_from_mem_region(t), prio, core) 172 | } else { 173 | PhantomResult(-1, PhantomData) 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /source/nwm_rs/src/vars.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | pub const ov_stats: *mut OVERLAY_STATS_INFO = 4 | (NS_CONFIG_ADDR as usize + mem::offset_of!(NS_CONFIG, ovStats)) as *mut OVERLAY_STATS_INFO; 5 | 6 | pub const rp_config: *mut RP_CONFIG = 7 | (NS_CONFIG_ADDR as usize + mem::offset_of!(NS_CONFIG, rpConfig)) as *mut RP_CONFIG; 8 | 9 | pub const rp_config_u32_count: usize = mem::size_of::() / mem::size_of::(); 10 | 11 | pub const ntr_config: *mut NTR_CONFIG = 12 | (NS_CONFIG_ADDR as usize + mem::offset_of!(NS_CONFIG, ntrConfig)) as *mut NTR_CONFIG; 13 | 14 | pub const THREAD_WAIT_NS: s64 = NWM_THREAD_WAIT_NS as s64; 15 | 16 | mod gpu; 17 | mod ranged; 18 | 19 | pub use gpu::*; 20 | pub use ranged::*; 21 | 22 | pub type CoreCount = IRanged; 23 | 24 | pub static mut thread_main_handle: Handle = 0; 25 | 26 | pub const SCREEN_COUNT: u32_ = RP_SCREEN_COUNT as u32_; 27 | pub const WORK_COUNT: u32_ = NWM_WORK_COUNT; 28 | 29 | pub type WorkIndex = Ranged; 30 | pub type ThreadId = Ranged; 31 | pub type ScreenIndex = Ranged; 32 | 33 | pub const fn IMG_BUFFER_SIZE(is_top: bool) -> usize { 34 | (GSP_SCREEN_WIDTH 35 | * 4 // max bpp 36 | * if is_top { 37 | GSP_SCREEN_HEIGHT_TOP 38 | } else { 39 | GSP_SCREEN_HEIGHT_BOTTOM 40 | }) as usize 41 | } 42 | pub const NWM_BUFFER_SIZE: usize = 43 | (SEND_BUFS_SIZE / WORK_COUNT) as usize / mem::size_of::() * mem::size_of::(); 44 | 45 | pub const RP_THREAD_PRIO_DEFAULT: u32 = RP_THREAD_PRIO_MAX; 46 | 47 | pub static mut home_process_handle: Handle = 0; 48 | 49 | #[derive(ConstDefault)] 50 | pub struct PerWorkHandles { 51 | pub nwm_done: Handle, 52 | pub nwm_ready: Handle, 53 | pub work_done_count: AtomicU32, 54 | pub work_done: Handle, 55 | pub work_begin_flag: AtomicBool, 56 | } 57 | 58 | type WorkHandles = RangedArray; 59 | 60 | #[derive(ConstDefault)] 61 | pub struct PerThreadHandles { 62 | pub work_ready: Handle, 63 | pub work_begin_ready: Handle, 64 | } 65 | 66 | type ThreadHandles = RangedArray; 67 | 68 | type PortScreenHandles = RangedArray; 69 | 70 | #[derive(ConstDefault)] 71 | pub struct SynHandles { 72 | pub works: WorkHandles, 73 | pub threads: ThreadHandles, 74 | 75 | pub nwm_ready: Handle, 76 | pub port_screen_ready: PortScreenHandles, 77 | pub screen_ready: Handle, 78 | } 79 | 80 | pub static mut syn_handles: *mut SynHandles = ptr::null_mut(); 81 | 82 | pub const fn J_MAX_HALF_FACTOR(v: u32_) -> u32_ { 83 | v / 2 84 | } 85 | 86 | pub static mut reliable_stream_cb: *mut rp_cb = const_default(); 87 | pub static mut reliable_stream_cb_lock: Handle = 0; 88 | pub static mut reliable_stream_cb_evt: Handle = 0; 89 | pub static mut seg_mem_sem: Handle = 0; 90 | pub static mut seg_mem_lock: Handle = 0; 91 | // pub static mut cur_seg_mem_sem: Handle = 0; 92 | // pub static mut cur_seg_mem_lock: Handle = 0; 93 | pub static mut term_seg_mem_sem: Handle = 0; 94 | pub static mut recv_seg_mem_inited: AtomicBool = const_default(); 95 | pub const RP_KCP_TIMEOUT_TICK: s32 = 2 * SYSCLOCK_ARM11 as s32; 96 | 97 | pub const RP_KCP_HDR_W_NBITS: u32 = 1; 98 | pub const RP_KCP_HDR_T_NBITS: u32 = 2; 99 | pub const RP_KCP_HDR_SIZE_NBITS: u32 = 11; 100 | pub const RP_KCP_HDR_RC_NBITS: u32 = 5; 101 | 102 | pub static mut delta_q_prev_coeffs: [*mut crate::jpeg::vars::JCoef; SCREEN_COUNT as usize] = 103 | const_default(); 104 | 105 | pub static mut wait_nwm_event: Handle = const_default(); 106 | pub static mut restart_ready_event: Handle = const_default(); 107 | pub static mut restart_done_event: Handle = const_default(); 108 | pub static mut restart_sleep: bool = const_default(); 109 | pub static mut restart_pending: bool = const_default(); 110 | -------------------------------------------------------------------------------- /source/nwm_rs/src/vars/gpu.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use super::{u32_, IoBaseLcd, IoBasePdc}; 4 | 5 | // From Luma3DS 6 | pub const GPU_FB_TOP_VSCANS: u32_ = IoBasePdc + 0x430; 7 | pub const GPU_FB_TOP_SIZE: u32_ = IoBasePdc + 0x45c; 8 | pub const GPU_FB_TOP_LEFT_ADDR_1: u32_ = IoBasePdc + 0x468; 9 | pub const GPU_FB_TOP_LEFT_ADDR_2: u32_ = IoBasePdc + 0x46C; 10 | pub const GPU_FB_TOP_FMT: u32_ = IoBasePdc + 0x470; 11 | pub const GPU_FB_TOP_CTRL: u32_ = IoBasePdc + 0x474; 12 | pub const GPU_FB_TOP_SEL: u32_ = IoBasePdc + 0x478; 13 | pub const GPU_FB_TOP_COL_LUT_INDEX: u32_ = IoBasePdc + 0x480; 14 | pub const GPU_FB_TOP_COL_LUT_ELEM: u32_ = IoBasePdc + 0x484; 15 | pub const GPU_FB_TOP_STRIDE: u32_ = IoBasePdc + 0x490; 16 | pub const GPU_FB_TOP_RIGHT_ADDR_1: u32_ = IoBasePdc + 0x494; 17 | pub const GPU_FB_TOP_RIGHT_ADDR_2: u32_ = IoBasePdc + 0x498; 18 | 19 | pub const GPU_FB_BOTTOM_VSCANS: u32_ = IoBasePdc + 0x530; 20 | pub const GPU_FB_BOTTOM_SIZE: u32_ = IoBasePdc + 0x55c; 21 | pub const GPU_FB_BOTTOM_ADDR_1: u32_ = IoBasePdc + 0x568; 22 | pub const GPU_FB_BOTTOM_ADDR_2: u32_ = IoBasePdc + 0x56C; 23 | pub const GPU_FB_BOTTOM_FMT: u32_ = IoBasePdc + 0x570; 24 | pub const GPU_FB_BOTTOM_CTRL: u32_ = IoBasePdc + 0x574; 25 | pub const GPU_FB_BOTTOM_SEL: u32_ = IoBasePdc + 0x578; 26 | pub const GPU_FB_BOTTOM_COL_LUT_INDEX: u32_ = IoBasePdc + 0x580; 27 | pub const GPU_FB_BOTTOM_COL_LUT_ELEM: u32_ = IoBasePdc + 0x584; 28 | pub const GPU_FB_BOTTOM_STRIDE: u32_ = IoBasePdc + 0x590; 29 | 30 | pub const GPU_VRAM_BANK_CTRL: u32_ = IoBasePdc + 0x30; 31 | pub const GPU_BACKLIGHT_CTRL: u32_ = IoBasePdc + 0xC0; 32 | 33 | pub const LCD_TOP_FILLCOLOR: u32_ = IoBaseLcd + 0x204; 34 | pub const LCD_BOTTOM_FILLCOLOR: u32_ = IoBaseLcd + 0xA04; 35 | -------------------------------------------------------------------------------- /source/nwm_rs/src/vars/ranged.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Copy, Clone, ConstDefault, ConstParamTy, Eq, PartialEq)] 4 | pub struct IRanged(u32_); 5 | 6 | impl Default for IRanged { 7 | fn default() -> Self { 8 | Self(BEG) 9 | } 10 | } 11 | 12 | pub struct IRangedIter(u32_); 13 | 14 | impl Iterator for IRangedIter 15 | where 16 | [(); (END - BEG) as usize]:, 17 | { 18 | type Item = IRanged; 19 | 20 | fn next(&mut self) -> Option { 21 | if self.0 > END { 22 | None 23 | } else { 24 | let r = unsafe { IRanged::::init_unchecked(self.0) }; 25 | self.0 += 1; 26 | Some(r) 27 | } 28 | } 29 | } 30 | 31 | pub struct IRangedIterN(u32_, u32_); 32 | 33 | impl Iterator for IRangedIterN 34 | where 35 | [(); (END - BEG) as usize]:, 36 | { 37 | type Item = IRanged; 38 | 39 | fn next(&mut self) -> Option { 40 | if self.0 >= self.1 { 41 | None 42 | } else { 43 | let r = unsafe { IRanged::::init_unchecked(self.0) }; 44 | self.0 += 1; 45 | Some(r) 46 | } 47 | } 48 | } 49 | 50 | #[allow(dead_code)] 51 | pub struct IRangedIterW(u32_, IRanged, u32_); 52 | 53 | impl Iterator for IRangedIterW 54 | where 55 | [(); (END - BEG) as usize]:, 56 | { 57 | type Item = IRanged; 58 | 59 | fn next(&mut self) -> Option { 60 | if self.0 == self.1 .0 { 61 | None 62 | } else { 63 | let r = unsafe { IRanged::::init_unchecked(self.0) }; 64 | self.0 += 1; 65 | if self.0 >= self.2 { 66 | self.0 = BEG 67 | } 68 | Some(r) 69 | } 70 | } 71 | } 72 | 73 | impl IRanged 74 | where 75 | [(); (END - BEG) as usize]:, 76 | { 77 | pub fn all() -> IRangedIter { 78 | IRangedIter::(BEG) 79 | } 80 | 81 | pub fn up_to(n: &IRanged) -> IRangedIterN 82 | where 83 | [(); { 1 - B2 } as usize]:, 84 | [(); { END + 1 - E2 } as usize]:, 85 | { 86 | Self::init().from_up_to(n) 87 | } 88 | 89 | pub fn from_up_to( 90 | &self, 91 | n: &IRanged, 92 | ) -> IRangedIterN 93 | where 94 | [(); { 1 - B2 } as usize]:, 95 | [(); { END + 1 - E2 } as usize]:, 96 | { 97 | IRangedIterN::(self.0, n.0) 98 | } 99 | 100 | pub const fn init() -> Self { 101 | Self(BEG) 102 | } 103 | 104 | #[allow(dead_code)] 105 | pub fn init_val() -> Self 106 | where 107 | [(); { N >= BEG } as usize - 1]:, 108 | [(); { N <= END } as usize - 1]:, 109 | { 110 | Self(N) 111 | } 112 | 113 | pub unsafe fn init_unchecked(v: u32_) -> Self { 114 | Self(v) 115 | } 116 | 117 | pub const fn get(&self) -> u32_ { 118 | self.0 119 | } 120 | 121 | pub fn set(&mut self, v: u32_) { 122 | if v < BEG { 123 | self.0 = BEG 124 | } else if v > END { 125 | self.0 = END 126 | } else { 127 | self.0 = v 128 | } 129 | } 130 | 131 | pub fn prev_wrapped(&mut self) { 132 | if self.0 == BEG { 133 | self.0 = END 134 | } else { 135 | self.0 -= 1 136 | } 137 | } 138 | 139 | pub fn next_wrapped(&mut self) { 140 | if self.0 == END { 141 | self.0 = BEG 142 | } else { 143 | self.0 += 1 144 | } 145 | } 146 | 147 | pub fn next_wrapped_n(&mut self, n: &IRanged) 148 | where 149 | [(); { 1 - B2 } as usize]:, 150 | [(); { END + 1 - E2 } as usize]:, 151 | { 152 | self.0 += 1; 153 | if self.0 >= n.0 { 154 | self.0 = BEG 155 | } 156 | } 157 | 158 | #[allow(dead_code)] 159 | pub fn index_into<'a, T, const N: usize>(&self, t: &'a [T; N]) -> &'a T 160 | where 161 | [(); END as usize - (N - 1)]:, 162 | { 163 | unsafe { t.get_unchecked(self.0 as usize) } 164 | } 165 | 166 | pub fn index_into_mut<'a, T, const N: usize>(&self, t: &'a mut [T; N]) -> &'a mut T 167 | where 168 | [(); END as usize - (N - 1)]:, 169 | { 170 | unsafe { t.get_unchecked_mut(self.0 as usize) } 171 | } 172 | } 173 | 174 | impl IRanged 175 | where 176 | [(); { 0 - BEG } as usize]:, 177 | [(); { END - 1 } as usize]:, 178 | [(); (END - BEG) as usize]:, 179 | { 180 | pub fn from_bool(v: bool) -> Self { 181 | unsafe { Self::init_unchecked(v as u32_) } 182 | } 183 | } 184 | 185 | pub type Ranged = IRanged<0, { N - 1 }>; 186 | 187 | #[allow(dead_code)] 188 | pub struct RangedArraySlice<'a, T, const N: u32_>(pub &'a mut RangedArray) 189 | where 190 | [(); N as usize]:; 191 | 192 | #[derive(ConstDefault)] 193 | pub struct RangedArray([T; N as usize]) 194 | where 195 | [(); N as usize]:; 196 | 197 | impl Clone for RangedArray 198 | where 199 | [(); N as usize]:, 200 | T: Clone, 201 | { 202 | fn clone_from(&mut self, source: &Self) { 203 | *self = source.clone() 204 | } 205 | 206 | fn clone(&self) -> Self { 207 | Self(self.0.clone()) 208 | } 209 | } 210 | 211 | impl Copy for RangedArray 212 | where 213 | [(); N as usize]:, 214 | T: Copy, 215 | { 216 | } 217 | 218 | impl RangedArray 219 | where 220 | [(); N as usize]:, 221 | { 222 | pub fn as_mut_ptr(&mut self) -> *mut T { 223 | self.0.as_mut_ptr() 224 | } 225 | 226 | pub fn get_r(&self, i: &Ranged) -> &T 227 | where 228 | [(); (N - N2) as usize]:, 229 | { 230 | unsafe { self.0.get_unchecked(i.0 as usize) } 231 | } 232 | 233 | pub fn get(&self, i: &Ranged) -> &T { 234 | unsafe { self.0.get_unchecked(i.0 as usize) } 235 | } 236 | 237 | pub fn get_mut(&mut self, i: &Ranged) -> &mut T { 238 | unsafe { self.0.get_unchecked_mut(i.0 as usize) } 239 | } 240 | 241 | pub fn get_b(&self, b: bool) -> &T 242 | where 243 | [(); { N - 2 } as usize]:, 244 | { 245 | unsafe { self.0.get_unchecked(b as usize) } 246 | } 247 | 248 | pub fn get_b_mut(&mut self, b: bool) -> &mut T 249 | where 250 | [(); { N - 2 } as usize]:, 251 | { 252 | unsafe { self.0.get_unchecked_mut(b as usize) } 253 | } 254 | 255 | #[allow(dead_code)] 256 | pub fn split_at_mut( 257 | &mut self, 258 | ) -> (RangedArraySlice, RangedArraySlice) 259 | where 260 | [(); I as usize]:, 261 | [(); { N - I } as usize]:, 262 | { 263 | unsafe { 264 | let a = self.0.as_mut_ptr(); 265 | let b = a.add(I as usize); 266 | ( 267 | RangedArraySlice::(mem::transmute(a)), 268 | RangedArraySlice::(mem::transmute(b)), 269 | ) 270 | } 271 | } 272 | 273 | #[allow(dead_code)] 274 | pub fn iter_mut(&mut self) -> impl Iterator + '_ { 275 | self.0.iter_mut() 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /source/proc.c: -------------------------------------------------------------------------------- 1 | #include "global.h" 2 | 3 | static u32 currentPid = 0; 4 | u32 getCurrentProcessId(void) { 5 | if (currentPid != 0) 6 | return currentPid; 7 | svcGetProcessId(¤tPid, CUR_PROCESS_HANDLE); 8 | return currentPid; 9 | } 10 | 11 | static Handle hCurrentProcess = 0; 12 | u32 getCurrentProcessHandle(void) { 13 | u32 handle = 0; 14 | u32 ret; 15 | 16 | if (hCurrentProcess != 0) { 17 | return hCurrentProcess; 18 | } 19 | ret = svcOpenProcess(&handle, getCurrentProcessId()); 20 | if (ret != 0) { 21 | return 0; 22 | } 23 | hCurrentProcess = handle; 24 | return hCurrentProcess; 25 | } 26 | 27 | u32 getProcessTIDByHandle(u32 hProcess, u32 tid[]) { 28 | u8 bufKProcess[0x100], bufKCodeSet[0x100]; 29 | u32 pKCodeSet, pKProcess; 30 | 31 | pKProcess = kGetKProcessByHandle(hProcess); 32 | if (pKProcess == 0) { 33 | tid[0] = tid[1] = 0; 34 | return 1; 35 | } 36 | 37 | kmemcpy(bufKProcess, (void *)pKProcess, 0x100); 38 | pKCodeSet = *(u32 *)&bufKProcess[KProcessCodesetOffset]; 39 | kmemcpy(bufKCodeSet, (void *)pKCodeSet, 0x100); 40 | u32 *pTid = (u32 *)&bufKCodeSet[0x5c]; 41 | tid[0] = pTid[0]; 42 | tid[1] = pTid[1]; 43 | 44 | return 0; 45 | } 46 | 47 | u32 mapRemoteMemory(Handle hProcess, u32 addr, u32 size, u32 op) { 48 | u32 outAddr = 0; 49 | u32 ret; 50 | 51 | u32 newKP = kGetKProcessByHandle(hProcess); 52 | u32 oldKP = kGetCurrentKProcess(); 53 | 54 | kSetCurrentKProcess(newKP); 55 | ret = svcControlMemory(&outAddr, addr, addr, size, op | MEMOP_REGION_SYSTEM, MEMPERM_READWRITE); 56 | kSetCurrentKProcess(oldKP); 57 | 58 | if (ret != 0) { 59 | return ret; 60 | } 61 | if (outAddr != addr) { 62 | showDbg("outAddr: %08"PRIx32", addr: %08"PRIx32"\n", outAddr, addr); 63 | return -1; 64 | } 65 | return 0; 66 | } 67 | 68 | u32 mapRemoteMemoryInLoader(Handle hProcess, u32 addr, u32 size, u32 op) { 69 | u32 outAddr = 0; 70 | u32 ret; 71 | u32 newKP = kGetKProcessByHandle(hProcess); 72 | u32 oldKP = kGetCurrentKProcess(); 73 | 74 | u32 oldPid = kSwapProcessPid(newKP, 1); 75 | 76 | kSetCurrentKProcess(newKP); 77 | ret = svcControlMemory(&outAddr, addr, addr, size, op | MEMOP_REGION_SYSTEM, MEMPERM_READWRITE); 78 | kSetCurrentKProcess(oldKP); 79 | kSwapProcessPid(newKP, oldPid); 80 | if (ret != 0) { 81 | return ret; 82 | } 83 | if (op == MEMOP_ALLOC && outAddr != addr) { 84 | showDbg("outAddr: %08"PRIx32", addr: %08"PRIx32"\n", outAddr, addr); 85 | return -1; 86 | } 87 | return 0; 88 | } 89 | 90 | u32 protectRemoteMemory(Handle hProcess, void *addr, u32 size, u32 perm) { 91 | return svcControlProcessMemory(hProcess, (u32)addr, 0, size, MEMOP_PROT, perm); 92 | } 93 | 94 | u32 protectMemory(void *addr, u32 size, u32 perm) { 95 | return protectRemoteMemory(getCurrentProcessHandle(), addr, size, perm); 96 | } 97 | 98 | u32 copyRemoteMemoryTimeout(Handle hDst, void *ptrDst, Handle hSrc, void *ptrSrc, u32 size, s64 timeout) { 99 | u8 dmaConfig[sizeof(DmaConfig)] = {-1, 0, 4}; 100 | u32 hdma = 0; 101 | u32 ret; 102 | 103 | ret = svcFlushProcessDataCache(hSrc, (u32)ptrSrc, size); 104 | if (ret != 0) { 105 | nsDbgPrint("svcFlushProcessDataCache src failed: %08"PRIx32"\n", ret); 106 | return ret; 107 | } 108 | ret = svcFlushProcessDataCache(hDst, (u32)ptrDst, size); 109 | if (ret != 0) { 110 | nsDbgPrint("svcFlushProcessDataCache dst failed: %08"PRIx32"\n", ret); 111 | return ret; 112 | } 113 | 114 | ret = svcStartInterProcessDma(&hdma, hDst, (u32)ptrDst, hSrc, (u32)ptrSrc, size, (DmaConfig *)dmaConfig); 115 | if (ret != 0) { 116 | nsDbgPrint("svcStartInterProcessDma failed: %08"PRIx32"\n", ret); 117 | return ret; 118 | } 119 | ret = svcWaitSynchronization(hdma, timeout); 120 | if (ret != 0) { 121 | showDbg("copyRemoteMemory time out (or error) %08"PRIx32, ret); 122 | svcCloseHandle(hdma); 123 | return 1; 124 | } 125 | 126 | svcCloseHandle(hdma); 127 | ret = svcInvalidateProcessDataCache(hDst, (u32)ptrDst, size); 128 | if (ret != 0) { 129 | nsDbgPrint("svcInvalidateProcessDataCache failed: %08"PRIx32"\n", ret); 130 | return ret; 131 | } 132 | return 0; 133 | } 134 | 135 | u32 copyRemoteMemory(Handle hDst, void *ptrDst, Handle hSrc, void *ptrSrc, u32 size) { 136 | return copyRemoteMemoryTimeout(hDst, ptrDst, hSrc, ptrSrc, size, COPY_REMOTE_MEMORY_TIMEOUT); 137 | } 138 | 139 | void showDbgMemInfo(Handle hProcess, u32 addr) { 140 | MemInfo memInfo; 141 | PageInfo pageInfo; 142 | s32 res = svcQueryProcessMemory(&memInfo, &pageInfo, hProcess, addr); 143 | if (res != 0) { 144 | showDbg("svcQueryProcessMemory failed for addr %08"PRIx32": %08"PRIx32, addr, res); 145 | } else { 146 | showDbg("addr %08"PRIx32": base %08"PRIx32", size: %08"PRIx32", perm: %"PRIu32", state: %"PRIu32", page: %08"PRIx32, 147 | addr, memInfo.base_addr, memInfo.size, memInfo.perm, memInfo.state, pageInfo.flags); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /source/rt.c: -------------------------------------------------------------------------------- 1 | #include "global.h" 2 | 3 | #include "3ds/services/soc.h" 4 | #include "3ds/services/fs.h" 5 | 6 | #include 7 | #include 8 | 9 | void rtInitLock(RT_LOCK *lock) { 10 | LightLock_Init(lock); 11 | } 12 | 13 | void rtAcquireLock(RT_LOCK *lock) { 14 | LightLock_Lock(lock); 15 | } 16 | 17 | void rtReleaseLock(RT_LOCK *lock) { 18 | LightLock_Unlock(lock); 19 | } 20 | 21 | void rtGenerateJumpCode(u32 dst, u32* buf) { 22 | buf[0] = 0xe51ff004; 23 | buf[1] = dst; 24 | } 25 | 26 | void rtGenerateJumpCodeThumbR3(u32 dst, u32 *buf) { 27 | buf[0] = 0x47184b00; 28 | buf[1] = dst; 29 | } 30 | 31 | void rtInitHookThumb(RT_HOOK* hook, u32 funcAddr, u32 callbackAddr) { 32 | *hook = (RT_HOOK){ 0 }; 33 | 34 | if (!funcAddr || !callbackAddr) { 35 | showDbg("Missing funcAddr or callbackAddr"); 36 | return; 37 | } 38 | 39 | s32 ret = rtCheckMemory(funcAddr, 8, MEMPERM_READWRITE | MEMPERM_EXECUTE); 40 | if (ret != 0) { 41 | showDbg("rtCheckMemory for addr %08"PRIx32" failed: %08"PRIx32, funcAddr, ret); 42 | return; 43 | } 44 | 45 | hook->model = 1; 46 | hook->funcAddr = funcAddr; 47 | rtGenerateJumpCodeThumbR3(callbackAddr, hook->jmpCode); 48 | } 49 | 50 | void rtInitHook(RT_HOOK *hook, u32 funcAddr, u32 callbackAddr) { 51 | *hook = (RT_HOOK){ 0 }; 52 | 53 | if (!funcAddr || !callbackAddr) { 54 | showDbg("Missing funcAddr or callbackAddr"); 55 | return; 56 | } 57 | 58 | s32 ret = rtCheckMemory(funcAddr, 8, MEMPERM_READWRITE | MEMPERM_EXECUTE); 59 | if (ret != 0) { 60 | showDbg("rtCheckMemory for addr %08"PRIx32" failed: %08"PRIx32, funcAddr, ret); 61 | return; 62 | } 63 | 64 | hook->funcAddr = funcAddr; 65 | memcpy(hook->bakCode, (void *)funcAddr, 8); 66 | rtGenerateJumpCode(callbackAddr, hook->jmpCode); 67 | memcpy(hook->callCode, (void *)funcAddr, 8); 68 | rtGenerateJumpCode(funcAddr + 8, &hook->callCode[2]); 69 | rtFlushInstructionCache(hook->callCode, 16); 70 | } 71 | 72 | void rtEnableHook(RT_HOOK *hook) { 73 | if (!hook->funcAddr || hook->isEnabled) { 74 | return; 75 | } 76 | memcpy((void *)hook->funcAddr, hook->jmpCode, 8); 77 | rtFlushInstructionCache((void *)hook->funcAddr, 8); 78 | hook->isEnabled = 1; 79 | } 80 | 81 | void rtDisableHook(RT_HOOK *hook) { 82 | if (!hook->funcAddr || !hook->isEnabled) { 83 | return; 84 | } 85 | memcpy((void *)hook->funcAddr, hook->bakCode, 8); 86 | rtFlushInstructionCache((void *)hook->funcAddr, 8); 87 | hook->isEnabled = 0; 88 | } 89 | 90 | u32 rtAlignToPageSize(u32 size) { 91 | return ALIGN_TO_PAGE_SIZE(size); 92 | } 93 | 94 | u32 rtGetPageOfAddress(u32 addr) { 95 | return PAGE_OF_ADDR(addr); 96 | } 97 | 98 | u32 rtCheckRemoteMemory(Handle hProcess, u32 addr, u32 size, MemPerm perm) { 99 | MemInfo memInfo; 100 | PageInfo pageInfo; 101 | s32 ret = svcQueryMemory(&memInfo, &pageInfo, addr); 102 | if (ret != 0) { 103 | nsDbgPrint("svcQueryMemory failed for addr %08"PRIx32": %08"PRIx32"\n", addr, ret); 104 | return ret; 105 | } 106 | if (memInfo.perm == 0) { 107 | return -1; 108 | } 109 | if (memInfo.base_addr + memInfo.size < addr + size) { 110 | return -1; 111 | } 112 | 113 | if (perm & MEMPERM_WRITE) 114 | perm |= MEMPERM_READ; 115 | if ((memInfo.perm & perm) == perm) { 116 | return 0; 117 | } 118 | 119 | perm |= memInfo.perm; 120 | 121 | u32 startPage, endPage; 122 | 123 | startPage = rtGetPageOfAddress(addr); 124 | endPage = rtGetPageOfAddress(addr + size - 1); 125 | size = endPage - startPage + 0x1000; 126 | 127 | ret = protectRemoteMemory(hProcess, (void *)startPage, size, perm); 128 | return ret; 129 | } 130 | 131 | u32 rtCheckMemory(u32 addr, u32 size, MemPerm perm) { 132 | return rtCheckRemoteMemory(getCurrentProcessHandle(), addr, size, perm); 133 | } 134 | 135 | u32 rtGetThreadReg(Handle hProcess, u32 tid, u32 *ctx) { 136 | u32 hThread; 137 | u32 pKThread, pContext; 138 | u32 ret; 139 | 140 | ret = svcOpenThread(&hThread, hProcess, tid); 141 | if (ret != 0) { 142 | return ret; 143 | } 144 | pKThread = kGetKProcessByHandle(hThread); 145 | kmemcpy(ctx, (void *)pKThread, 160); 146 | pContext = ctx[0x8c / 4] - 0x10c; 147 | kmemcpy(ctx, (void *)pContext, 0x10c); 148 | svcCloseHandle(hThread); 149 | return 0; 150 | } 151 | 152 | u32 rtFlushInstructionCache(void *ptr, u32 size) { 153 | return svcFlushProcessDataCache(CUR_PROCESS_HANDLE, (u32)ptr, size); 154 | } 155 | 156 | int rtRecvSocket(u32 sockfd, u8 *buf, int size) 157 | { 158 | int ret, pos = 0; 159 | int tmpsize = size; 160 | 161 | while(tmpsize) 162 | { 163 | if((ret = recv(sockfd, &buf[pos], tmpsize, 0)) <= 0) 164 | { 165 | if (ret < 0) { 166 | ret = errno; 167 | if (ret == EWOULDBLOCK || ret == EAGAIN) { 168 | svcSleepThread(50000000); 169 | continue; 170 | } 171 | return -1; 172 | } 173 | return ret; 174 | } 175 | pos += ret; 176 | tmpsize -= ret; 177 | } 178 | 179 | return size; 180 | } 181 | 182 | int rtSendSocket(u32 sockfd, u8 *buf, int size) 183 | { 184 | int ret, pos = 0; 185 | int tmpsize = size; 186 | 187 | while(tmpsize) 188 | { 189 | if((ret = send(sockfd, &buf[pos], tmpsize, 0)) < 0) 190 | { 191 | ret = errno; 192 | if (ret == EWOULDBLOCK || ret == EAGAIN) { 193 | svcSleepThread(50000000); 194 | continue; 195 | } 196 | return -1; 197 | } 198 | pos += ret; 199 | tmpsize -= ret; 200 | } 201 | 202 | return size; 203 | } 204 | 205 | Handle rtOpenFile(char *fileName) { 206 | Handle file; 207 | s32 ret = FSUSER_OpenFileDirectly(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, NULL), fsMakePath(PATH_ASCII, fileName), FS_OPEN_READ, 0); 208 | if (ret != 0) { 209 | nsDbgPrint("Failed to open file %s: %08"PRIx32"\n", fileName, ret); 210 | return 0; 211 | } 212 | return file; 213 | } 214 | 215 | Handle rtOpenFile16(u16 *fileName) { 216 | Handle file; 217 | s32 ret = FSUSER_OpenFileDirectly(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, NULL), fsMakePath(PATH_UTF16, fileName), FS_OPEN_READ, 0); 218 | if (ret != 0) { 219 | nsDbgPrint("Failed to open file: %08"PRIx32"\n", ret); 220 | return 0; 221 | } 222 | return file; 223 | } 224 | 225 | u32 rtGetFileSize(Handle file) { 226 | u64 fileSize; 227 | s32 ret = FSFILE_GetSize(file, &fileSize); 228 | if (ret != 0) { 229 | nsDbgPrint("Failed to get file size: %08"PRIx32"\n", ret); 230 | return 0; 231 | } 232 | return (u32)fileSize; 233 | } 234 | 235 | u32 rtLoadFileToBuffer(Handle file, void *pBuf, u32 bufSize) { 236 | u32 bytesRead; 237 | s32 ret = FSFILE_Read(file, &bytesRead, 0, (void *)pBuf, bufSize); 238 | if (ret != 0) { 239 | nsDbgPrint("Failed to read file: %08"PRIx32"\n", ret); 240 | return 0; 241 | } 242 | return bytesRead; 243 | } 244 | 245 | void rtCloseFile(Handle file) { 246 | FSFILE_Close(file); 247 | } 248 | -------------------------------------------------------------------------------- /source/svc7b.c: -------------------------------------------------------------------------------- 1 | #include "global.h" 2 | 3 | u32 KProcessHandleDataOffset; 4 | u32 KProcessPIDOffset; 5 | u32 KProcessCodesetOffset; 6 | void InvalidateEntireInstructionCache(void); 7 | void InvalidateEntireDataCache(void); 8 | 9 | static u32 keRefHandle(u32 pHandleTable, u32 handle) { 10 | u32 handleLow = handle & 0x7fff; 11 | u32 ptr = *(u32 *)(*(u32 *)pHandleTable + (handleLow * 8) + 4); 12 | return ptr; 13 | } 14 | 15 | static u32 *translateAddress(u32 addr) { 16 | if (addr < 0x1ff00000) { 17 | return (u32*)(addr - 0x1f3f8000 + 0xfffdc000); 18 | } 19 | return (u32*)(addr - 0x1ff00000 + 0xdff00000); 20 | } 21 | 22 | static void set_kmmu_rw(int cpu, u32 addr, u32 size) 23 | { 24 | int i, j; 25 | u32 mmu_p; 26 | u32 p1, p2; 27 | u32 v1, v2; 28 | u32 end; 29 | 30 | if (cpu == 0) { 31 | mmu_p = 0x1fff8000; 32 | } 33 | if (cpu == 1) { 34 | mmu_p = 0x1fffc000; 35 | } 36 | if (cpu == 2) { 37 | mmu_p = 0x1f3f8000; 38 | } 39 | 40 | end = addr + size; 41 | 42 | v1 = 0x20000000; 43 | for (i = 512; i < 4096; ++i) { 44 | p1 = *translateAddress(mmu_p + i * 4); 45 | if ((p1 & 3) == 2) { 46 | if (v1 >= addr && v1 < end) { 47 | p1 &= 0xffff73ff; 48 | p1 |= 0x00000c00; 49 | *translateAddress(mmu_p + i * 4) = p1; 50 | } 51 | } 52 | else if ((p1 & 3) == 1) { 53 | p1 &= 0xfffffc00; 54 | for (j = 0; j < 256; ++j) { 55 | v2 = v1 + j * 0x1000; 56 | if ((v2 >= addr) && (v2 < end)) { 57 | p2 = *translateAddress(p1 + j * 4); 58 | if ((p2 & 3) == 1) { 59 | p2 &= 0xffff7dcf; 60 | p2 |= 0x00000030; 61 | *translateAddress(p1 + j * 4) = p2; 62 | } 63 | else if ((p2 & 3) > 1) { 64 | p2 &= 0xfffffdce; 65 | p2 |= 0x00000030; 66 | *translateAddress(p1 + j * 4) = p2; 67 | } 68 | } 69 | } 70 | } 71 | v1 += 0x00100000; 72 | } 73 | } 74 | 75 | enum { 76 | KCALL_KMEMCPY = 1, 77 | }; 78 | 79 | void kememcpy(void *dst, void *src, u32 size) { 80 | u32 i; 81 | for (i = 0; i < size; i += 4) { 82 | *(vu32 *)(dst + i) = *(vu32 *)(src + i); 83 | } 84 | } 85 | 86 | u32 keGetKProcessByHandle(u32 handle) { 87 | return keRefHandle(*(u32 *)0xFFFF9004 + KProcessHandleDataOffset, handle); 88 | } 89 | 90 | u32 keGetCurrentKProcess(void) { 91 | return *(u32 *)0xFFFF9004; 92 | } 93 | 94 | void keSetCurrentKProcess(u32 ptr) { 95 | *(u32 *)0xFFFF9004 = ptr; 96 | } 97 | 98 | u32 keSwapProcessPid(u32 kProcess, u32 newPid) { 99 | u32 oldPid = *(u32*)(kProcess + KProcessPIDOffset); 100 | *(u32*)(kProcess + KProcessPIDOffset) = newPid; 101 | return oldPid; 102 | } 103 | 104 | void keDoKernelHax(NTR_CONFIG *ntrCfg) { 105 | // set mmu 106 | 107 | set_kmmu_rw(0, ntrCfg->KMMUHaxAddr, ntrCfg->KMMUHaxSize); 108 | set_kmmu_rw(1, ntrCfg->KMMUHaxAddr, ntrCfg->KMMUHaxSize); 109 | if (ntrCfg->isNew3DS) { 110 | set_kmmu_rw(2, ntrCfg->KMMUHaxAddr, ntrCfg->KMMUHaxSize); 111 | } 112 | 113 | // set_remoteplay_mmu(0xd8000000, 0x00600000); 114 | /* patch controlmemory to disable address boundary check */ 115 | 116 | *(u32*)(ntrCfg->ControlMemoryPatchAddr1) = 0; 117 | *(u32*)(ntrCfg->ControlMemoryPatchAddr2) = 0; 118 | 119 | InvalidateEntireInstructionCache(); 120 | InvalidateEntireDataCache(); 121 | } 122 | -------------------------------------------------------------------------------- /source/ui.c: -------------------------------------------------------------------------------- 1 | #include "global.h" 2 | 3 | #include "3ds/ipc.h" 4 | 5 | #include 6 | 7 | static Handle menuSessionClient; 8 | Handle menuGetPortHandle(void) { 9 | Handle hClient = menuSessionClient; 10 | s32 ret; 11 | if (hClient == 0) { 12 | ret = svcConnectToPort(&hClient, SVC_PORT_MENU); 13 | if (ret != 0) { 14 | return 0; 15 | } 16 | menuSessionClient = hClient; 17 | } 18 | return hClient; 19 | } 20 | 21 | int showMsgVerbose(const char *file_name, int line_number, const char *func_name, const char *fmt, ...) { 22 | va_list arp; 23 | va_start(arp, fmt); 24 | s32 ret = showMsgVA(file_name, line_number, func_name, fmt, arp); 25 | va_end(arp); 26 | return ret; 27 | } 28 | 29 | int showMsgRaw(const char *fmt, ...) { 30 | va_list arp; 31 | va_start(arp, fmt); 32 | s32 ret = showMsgVA(NULL, 0, NULL, fmt, arp); 33 | va_end(arp); 34 | return ret; 35 | } 36 | 37 | void __attribute__((weak)) showMsgRaw2(const char *title, const char *msg) { 38 | u32* cmdbuf = getThreadCommandBuffer(); 39 | cmdbuf[0] = IPC_MakeHeader(SVC_MENU_CMD_SHOW_MSG, 0, 4); 40 | cmdbuf[1] = IPC_Desc_StaticBuffer(strlen(title) + 1, 0); 41 | cmdbuf[2] = (u32)title; 42 | cmdbuf[3] = IPC_Desc_StaticBuffer(strlen(msg) + 1, 1); 43 | cmdbuf[4] = (u32)msg; 44 | 45 | s32 ret = svcSendSyncRequest(menuGetPortHandle()); 46 | if (ret != 0) { 47 | disp(100, DBG_CL_MSG); 48 | svcSleepThread(1000000000); 49 | return; 50 | } 51 | return; 52 | } 53 | 54 | int __attribute__((weak)) showMsgVAPre() { 55 | Handle hClient = menuGetPortHandle(); 56 | if (!hClient) { 57 | disp(100, DBG_CL_MSG); 58 | svcSleepThread(1000000000); 59 | return -1; 60 | } 61 | return 0; 62 | } 63 | 64 | void printTitleAndMsg(char title[LOCAL_TITLE_BUF_SIZE], const char *file_name, int line_number, const char *func_name, char msg[LOCAL_MSG_BUF_SIZE], const char* fmt, va_list va) { 65 | if (file_name && func_name) { 66 | u64 ticks = svcGetSystemTick(); 67 | u64 mono_ns = ticks * 1000000000 / SYSCLOCK_ARM11; 68 | u32 pid = getCurrentProcessId(); 69 | xsnprintf(title, LOCAL_TITLE_BUF_SIZE, DBG_VERBOSE_TITLE, (u32)(mono_ns / 1000000 % 60000), (u32)(mono_ns % 1000000), pid, file_name, line_number, func_name); 70 | } else { 71 | *title = 0; 72 | } 73 | xvsnprintf(msg, LOCAL_MSG_BUF_SIZE, fmt, va); 74 | } 75 | 76 | int __attribute__((weak)) showMsgVA(const char *file_name, int line_number, const char *func_name, const char* fmt, va_list va) { 77 | s32 ret = showMsgVAPre(); 78 | if (ret != 0) 79 | return 0; 80 | 81 | char title[LOCAL_TITLE_BUF_SIZE]; 82 | char msg[LOCAL_MSG_BUF_SIZE]; 83 | printTitleAndMsg(title, file_name, line_number, func_name, msg, fmt, va); 84 | 85 | showMsgRaw2(title, msg); 86 | 87 | return 0; 88 | } 89 | 90 | void disp(u32 t, u32 cl) { 91 | u32 i; 92 | 93 | for (i = 0; i < t; ++i){ 94 | REG(LCD_TOP_FILLCOLOR) = cl; 95 | svcSleepThread(5000000); 96 | } 97 | REG(LCD_TOP_FILLCOLOR) = 0; 98 | } 99 | 100 | void panicHandle(const char *file, int file_len, int line, int column) { 101 | char file_c[LOCAL_MSG_BUF_SIZE]; 102 | file_len = file_len > LOCAL_MSG_BUF_SIZE - 1 ? LOCAL_MSG_BUF_SIZE - 1 : file_len; 103 | memcpy(file_c, file, file_len); 104 | file_c[file_len] = 0; 105 | showMsgRaw("Panic: %s:%d:%d", file_c, line, column); 106 | } 107 | --------------------------------------------------------------------------------