mirror of
https://github.com/ITotalJustice/sys-patch.git
synced 2025-01-05 10:41:49 +01:00
add overlay, fix es 14.0.0-14.1.2 patches, fix b_patch, add logging
This commit is contained in:
parent
5d62cf2cdb
commit
7d2a332bdd
2
.github/workflows/build_depoly.yml
vendored
2
.github/workflows/build_depoly.yml
vendored
@ -9,6 +9,8 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout 🛎️
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Build
|
||||
run: make dist -j2
|
||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,6 +1,6 @@
|
||||
.vscode
|
||||
build
|
||||
Firmware
|
||||
firmware
|
||||
420000000000000B
|
||||
*.txt
|
||||
*.elf
|
||||
@ -8,5 +8,9 @@ Firmware
|
||||
*.nso
|
||||
*.nsp
|
||||
*.zip
|
||||
*.nacp
|
||||
*.ovl
|
||||
*.nca
|
||||
out
|
||||
ignoreme
|
||||
.vscode/settings.json
|
||||
|
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
[submodule "overlay/libtesla"]
|
||||
path = overlay/libtesla
|
||||
url = https://github.com/ITotalJustice/libtesla.git
|
||||
branch = tj
|
20
.vscode/c_cpp_properties.json
vendored
Normal file
20
.vscode/c_cpp_properties.json
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "switch",
|
||||
"includePath": [
|
||||
"${default}",
|
||||
"${workspaceFolder}/**",
|
||||
"${DEVKITPRO}/libnx/include/",
|
||||
"${DEVKITPRO}/portlibs/switch/include/",
|
||||
"${workspaceFolder}/overlay/libtesla/include",
|
||||
"${workspaceFolder}/common"
|
||||
],
|
||||
"defines": [],
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++23",
|
||||
"compilerPath": "${DEVKITPRO}/devkitA64/bin/aarch64-none-elf-gcc"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
236
Makefile
236
Makefile
@ -1,233 +1,23 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
MAKEFILES := sysmod overlay
|
||||
TARGETS := $(foreach dir,$(MAKEFILES),$(CURDIR)/$(dir))
|
||||
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
all: $(TARGETS)
|
||||
@mkdir -p out/
|
||||
@cp -R sysmod/out/* out/
|
||||
@cp -R overlay/out/* out/
|
||||
|
||||
TOPDIR ?= $(CURDIR)
|
||||
include $(DEVKITPRO)/libnx/switch_rules
|
||||
.PHONY: $(TARGETS)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data files
|
||||
# INCLUDES is a list of directories containing header files
|
||||
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
|
||||
#
|
||||
# NO_ICON: if set to anything, do not use icon.
|
||||
# NO_NACP: if set to anything, no .nacp file is generated.
|
||||
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
|
||||
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
|
||||
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
|
||||
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
|
||||
# ICON is the filename of the icon (.jpg), relative to the project folder.
|
||||
# If not set, it attempts to use one of the following (in this order):
|
||||
# - <Project name>.jpg
|
||||
# - icon.jpg
|
||||
# - <libnx folder>/default_icon.jpg
|
||||
#
|
||||
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
|
||||
# If not set, it attempts to use one of the following (in this order):
|
||||
# - <Project name>.json
|
||||
# - config.json
|
||||
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
|
||||
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
|
||||
# NACP building is skipped as well.
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := sys-patch
|
||||
BUILD := build
|
||||
SOURCES := src src/minIni
|
||||
DATA := data
|
||||
INCLUDES := include
|
||||
#ROMFS := romfs
|
||||
$(TARGETS):
|
||||
@$(MAKE) -C $@
|
||||
|
||||
# sys-patch
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||
|
||||
CFLAGS := -g -Wall -O2 -ffunction-sections \
|
||||
$(ARCH) $(DEFINES)
|
||||
|
||||
CFLAGS += $(INCLUDE) -D__SWITCH__
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -std=c++23 -fno-rtti -fno-exceptions
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lnx
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
|
||||
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
ifeq ($(strip $(CONFIG_JSON)),)
|
||||
jsons := $(wildcard *.json)
|
||||
ifneq (,$(findstring $(TARGET).json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/$(TARGET).json
|
||||
else
|
||||
ifneq (,$(findstring config.json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/config.json
|
||||
endif
|
||||
endif
|
||||
else
|
||||
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(ICON)),)
|
||||
icons := $(wildcard *.jpg)
|
||||
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
|
||||
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
|
||||
else
|
||||
ifneq (,$(findstring icon.jpg,$(icons)))
|
||||
export APP_ICON := $(TOPDIR)/icon.jpg
|
||||
endif
|
||||
endif
|
||||
else
|
||||
export APP_ICON := $(TOPDIR)/$(ICON)
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(NO_ICON)),)
|
||||
export NROFLAGS += --icon=$(APP_ICON)
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(NO_NACP)),)
|
||||
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
|
||||
endif
|
||||
|
||||
ifneq ($(APP_TITLEID),)
|
||||
export NACPFLAGS += --titleid=$(APP_TITLEID)
|
||||
endif
|
||||
|
||||
ifneq ($(ROMFS),)
|
||||
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
|
||||
endif
|
||||
|
||||
.PHONY: $(BUILD) clean all
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
@rm -rf out/
|
||||
@mkdir -p out/atmosphere/contents/420000000000000B/flags
|
||||
@touch out/atmosphere/contents/420000000000000B/flags/boot2.flag
|
||||
@cp $(CURDIR)/toolbox.json out/atmosphere/contents/420000000000000B/toolbox.json
|
||||
@cp $(CURDIR)/$(TARGET).nsp out/atmosphere/contents/420000000000000B/exefs.nsp
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
ifeq ($(strip $(APP_JSON)),)
|
||||
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
|
||||
else
|
||||
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
@rm -rf out
|
||||
@for i in $(TARGETS); do $(MAKE) -C $$i clean || exit 1; done;
|
||||
|
||||
dist: all
|
||||
@for i in $(TARGETS); do $(MAKE) -C $$i dist || exit 1; done;
|
||||
@echo making dist ...
|
||||
|
||||
@rm -f sys-patch.zip
|
||||
@cd out; zip -r ../sys-patch.zip ./*; cd ../
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(APP_JSON)),)
|
||||
|
||||
all : $(OUTPUT).nro
|
||||
|
||||
ifeq ($(strip $(NO_NACP)),)
|
||||
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
|
||||
else
|
||||
$(OUTPUT).nro : $(OUTPUT).elf
|
||||
endif
|
||||
|
||||
else
|
||||
|
||||
all : $(OUTPUT).nsp
|
||||
|
||||
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
|
||||
|
||||
$(OUTPUT).nso : $(OUTPUT).elf
|
||||
|
||||
endif
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
$(OFILES_SRC) : $(HFILES_BIN)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o %_bin.h : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
||||
|
24
README.md
24
README.md
@ -6,25 +6,41 @@ A script-like system module that patches fs, es and ldr on boot.
|
||||
|
||||
## Config
|
||||
|
||||
sys-patch features a *very* simple config, only 2 options so far and they both do the same thing :p
|
||||
sys-patch features a simple config. This can be manually editied or updated using the overlay.
|
||||
|
||||
this config file can be found in `/config/sys-patch/config.ini`, if the file does not exist, the file will be created when sys-patch is run.
|
||||
the config file can be found in `/config/sys-patch/config.ini`, if the file does not exist, the file will be created when sys-patch is run.
|
||||
|
||||
```ini
|
||||
[options]
|
||||
patch_sysmmc=1 ; 1=(default) patch sysmmc, 0=don't patch sysmmc
|
||||
patch_emummc=1 ; 1=(default) patch emummc, 0=don't patch emummc
|
||||
logging=1 ; 1=(default) output /config/sys-patch/log.inim 0=no log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Overlay
|
||||
|
||||
the overlay can be used to change the config options and to see what patches are applied (if any).
|
||||
|
||||
- Unpatched means the patch wasn't applied (likely not found).
|
||||
- Patched (green) means it was patched by sys-patch.
|
||||
- Patched (yellow) means it was already patched, likely by sigpatches or a custom atmosphere build.
|
||||
|
||||
<p float="left">
|
||||
<img src="https://i.imgur.com/IlTkkYM.jpg" width="400" />
|
||||
<img src="https://i.imgur.com/T4K5u5f.jpg" width="400" />
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
## Building
|
||||
|
||||
### prerequisites
|
||||
- install devkitpro
|
||||
|
||||
```sh
|
||||
git clone https://github.com/ITotalJustice/sys-patch.git
|
||||
git clone --recurse-submodules https://github.com/ITotalJustice/sys-patch.git
|
||||
cd sys-patch
|
||||
make
|
||||
```
|
||||
@ -77,7 +93,6 @@ This repo is mainly a proof of concept. I would love for someone to build upon t
|
||||
|
||||
here are a few ideas that i have:
|
||||
- option to load new patterns from file
|
||||
- make this into a service / overlay
|
||||
- make homebrew frontend that can update this sysmod, apply patches, all without having to reboot
|
||||
|
||||
---
|
||||
@ -97,3 +112,4 @@ software is built on the shoulders of giants. this tool wouldn't be possible wth
|
||||
- Switchbrew (libnx, switch-examples)
|
||||
- DevkitPro (toolchain)
|
||||
- [minIni](https://github.com/compuphase/minIni)
|
||||
- [libtesla](https://github.com/WerWolv/libtesla)
|
||||
|
@ -46,7 +46,6 @@ bool ini_openrewrite(const char* filename, struct NxFile* nxfile) {
|
||||
|
||||
bool ini_close(struct NxFile* nxfile) {
|
||||
fsFileClose(&nxfile->file);
|
||||
fsFsCommit(&nxfile->system);
|
||||
fsFsClose(&nxfile->system);
|
||||
return true;
|
||||
}
|
@ -16,7 +16,6 @@ struct NxFile {
|
||||
#define INI_FILEPOS s64
|
||||
#define INI_OPENREWRITE
|
||||
#define INI_REMOVE
|
||||
#define INI_NOBROWSE
|
||||
|
||||
bool ini_openread(const char* filename, struct NxFile* nxfile);
|
||||
bool ini_openwrite(const char* filename, struct NxFile* nxfile);
|
225
overlay/Makefile
Normal file
225
overlay/Makefile
Normal file
@ -0,0 +1,225 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
|
||||
TOPDIR ?= $(CURDIR)
|
||||
include $(DEVKITPRO)/libnx/switch_rules
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data files
|
||||
# INCLUDES is a list of directories containing header files
|
||||
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
|
||||
#
|
||||
# NO_ICON: if set to anything, do not use icon.
|
||||
# NO_NACP: if set to anything, no .nacp file is generated.
|
||||
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
|
||||
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
|
||||
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
|
||||
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
|
||||
# ICON is the filename of the icon (.jpg), relative to the project folder.
|
||||
# If not set, it attempts to use one of the following (in this order):
|
||||
# - <Project name>.jpg
|
||||
# - icon.jpg
|
||||
# - <libnx folder>/default_icon.jpg
|
||||
#
|
||||
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
|
||||
# If not set, it attempts to use one of the following (in this order):
|
||||
# - <Project name>.json
|
||||
# - config.json
|
||||
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
|
||||
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
|
||||
# NACP building is skipped as well.
|
||||
#---------------------------------------------------------------------------------
|
||||
APP_TITLE := sys-patch
|
||||
APP_AUTHOR := TotalJustice
|
||||
APP_VERSION := 1.2.0
|
||||
|
||||
TARGET := sys-patch-overlay
|
||||
BUILD := build
|
||||
SOURCES := src ../common/minIni
|
||||
DATA := data
|
||||
INCLUDES := include ../common libtesla/include
|
||||
|
||||
NO_ICON := 1
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||
|
||||
CFLAGS := -g -Wall -O2 -ffunction-sections \
|
||||
$(ARCH) $(DEFINES)
|
||||
|
||||
CFLAGS += $(INCLUDE) -D__SWITCH__
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -std=c++23 -fno-exceptions
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lnx
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
|
||||
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
ifeq ($(strip $(CONFIG_JSON)),)
|
||||
jsons := $(wildcard *.json)
|
||||
ifneq (,$(findstring $(TARGET).json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/$(TARGET).json
|
||||
else
|
||||
ifneq (,$(findstring config.json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/config.json
|
||||
endif
|
||||
endif
|
||||
else
|
||||
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(ICON)),)
|
||||
icons := $(wildcard *.jpg)
|
||||
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
|
||||
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
|
||||
else
|
||||
ifneq (,$(findstring icon.jpg,$(icons)))
|
||||
export APP_ICON := $(TOPDIR)/icon.jpg
|
||||
endif
|
||||
endif
|
||||
else
|
||||
export APP_ICON := $(TOPDIR)/$(ICON)
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(NO_ICON)),)
|
||||
export NROFLAGS += --icon=$(APP_ICON)
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(NO_NACP)),)
|
||||
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
|
||||
endif
|
||||
|
||||
ifneq ($(APP_TITLEID),)
|
||||
export NACPFLAGS += --titleid=$(APP_TITLEID)
|
||||
endif
|
||||
|
||||
ifneq ($(ROMFS),)
|
||||
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
|
||||
endif
|
||||
|
||||
.PHONY: $(BUILD) clean all
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
|
||||
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
@mkdir -p out/switch/.overlays
|
||||
@cp $(CURDIR)/$(TARGET).ovl out/switch/.overlays/
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(TARGET).ovl $(TARGET).nro $(TARGET).nacp $(TARGET).elf
|
||||
@rm -rf out/
|
||||
@rm -f sys-patch-overlay.zip
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
ftp: all
|
||||
@echo making dist ...
|
||||
curl -T sys-patch-overlay.ovl ftp://192.168.200.71:5000/switch/.overlays/ --user tj:12345678
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
dist: all
|
||||
@echo making dist ...
|
||||
|
||||
@rm -f sys-patch-overlay.zip
|
||||
@cd out; zip -r ../sys-patch-overlay.zip ./*; cd ../
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all : $(OUTPUT).ovl
|
||||
|
||||
$(OUTPUT).ovl : $(OUTPUT).elf $(OUTPUT).nacp
|
||||
@elf2nro $< $@ $(NROFLAGS)
|
||||
@echo "built ... $(notdir $(OUTPUT).ovl)"
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
$(OFILES_SRC) : $(HFILES_BIN)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o %_bin.h : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
1
overlay/libtesla
Submodule
1
overlay/libtesla
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 3d74aeb31f0f26cf222d406c7ea118a82cc8ee69
|
149
overlay/src/main.cpp
Normal file
149
overlay/src/main.cpp
Normal file
@ -0,0 +1,149 @@
|
||||
#define TESLA_INIT_IMPL // If you have more than one file using the tesla header, only define this in the main one
|
||||
#include <tesla.hpp> // The Tesla Header
|
||||
#include <string_view>
|
||||
#include "minIni/minIni.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto CONFIG_PATH = "/config/sys-patch/config.ini";
|
||||
constexpr auto LOG_PATH = "/config/sys-patch/log.ini";
|
||||
|
||||
auto does_file_exist(const char* path) -> bool {
|
||||
Result rc{};
|
||||
FsFileSystem fs{};
|
||||
FsFile file{};
|
||||
char path_buf[FS_MAX_PATH]{};
|
||||
|
||||
if (R_FAILED(fsOpenSdCardFileSystem(&fs))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
strcpy(path_buf, path);
|
||||
rc = fsFsOpenFile(&fs, path_buf, FsOpenMode_Read, &file);
|
||||
fsFileClose(&file);
|
||||
fsFsClose(&fs);
|
||||
return R_SUCCEEDED(rc);
|
||||
}
|
||||
|
||||
// creates a directory, non-recursive!
|
||||
auto create_dir(const char* path) -> bool {
|
||||
Result rc{};
|
||||
FsFileSystem fs{};
|
||||
char path_buf[FS_MAX_PATH]{};
|
||||
|
||||
if (R_FAILED(fsOpenSdCardFileSystem(&fs))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
strcpy(path_buf, path);
|
||||
rc = fsFsCreateDirectory(&fs, path_buf);
|
||||
fsFsClose(&fs);
|
||||
return R_SUCCEEDED(rc);
|
||||
}
|
||||
|
||||
struct ConfigEntry {
|
||||
const char* const section;
|
||||
const char* const key;
|
||||
bool value;
|
||||
|
||||
ConfigEntry(const char* _section, const char* _key, bool default_value) :
|
||||
section{_section}, key{_key}, value{default_value} {}
|
||||
|
||||
void load_value_from_ini() {
|
||||
this->value = ini_getbool(this->section, this->key, this->value, CONFIG_PATH);
|
||||
}
|
||||
|
||||
auto create_list_item(const char* text) {
|
||||
auto item = new tsl::elm::ToggleListItem(text, value);
|
||||
item->setStateChangedListener([this](bool new_value){
|
||||
this->value = new_value;
|
||||
ini_putl(this->section, this->key, this->value, CONFIG_PATH);
|
||||
});
|
||||
return item;
|
||||
}
|
||||
};
|
||||
|
||||
class GuiMain final : public tsl::Gui {
|
||||
public:
|
||||
GuiMain() { }
|
||||
|
||||
// Called when this Gui gets loaded to create the UI
|
||||
// Allocate all elements on the heap. libtesla will make sure to clean them up when not needed anymore
|
||||
tsl::elm::Element* createUI() override {
|
||||
create_dir("/config/");
|
||||
create_dir("/config/sys-patch/");
|
||||
|
||||
config_patch_sysmmc.load_value_from_ini();
|
||||
config_patch_emummc.load_value_from_ini();
|
||||
config_logging.load_value_from_ini();
|
||||
|
||||
auto frame = new tsl::elm::OverlayFrame("sys-patch", "v1.2.0");
|
||||
auto list = new tsl::elm::List();
|
||||
|
||||
list->addItem(new tsl::elm::CategoryHeader("Options"));
|
||||
list->addItem(config_patch_sysmmc.create_list_item("Patch SysMMC"));
|
||||
list->addItem(config_patch_emummc.create_list_item("Patch EmuMMC"));
|
||||
list->addItem(config_logging.create_list_item("Logging"));
|
||||
|
||||
if (does_file_exist(LOG_PATH)) {
|
||||
struct CallbackUser {
|
||||
tsl::elm::List* list;
|
||||
std::string last_section;
|
||||
} callback_userdata{list};
|
||||
|
||||
ini_browse([](const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, void *UserData){
|
||||
auto user = (CallbackUser*)UserData;
|
||||
if (!std::strcmp("Skipped", Value)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (user->last_section != Section) {
|
||||
user->last_section = Section;
|
||||
user->list->addItem(new tsl::elm::CategoryHeader("Log: " + user->last_section));
|
||||
}
|
||||
|
||||
#define F(x) ((x) >> 4) // 8bit -> 4bit
|
||||
constexpr tsl::Color colour_syspatch{F(0), F(255), F(200), F(255)};
|
||||
constexpr tsl::Color colour_file{F(255), F(177), F(66), F(255)};
|
||||
constexpr tsl::Color colour_unpatched{F(250), F(90), F(58), F(255)};
|
||||
#undef F
|
||||
|
||||
std::string_view value{Value};
|
||||
if (value.starts_with("Patched")) {
|
||||
if (value.ends_with("(sys-patch)")) {
|
||||
user->list->addItem(new tsl::elm::ListItem(Key, "Patched", colour_syspatch));
|
||||
} else {
|
||||
user->list->addItem(new tsl::elm::ListItem(Key, "Patched", colour_file));
|
||||
}
|
||||
} else if (value.starts_with("Unpatched")) {
|
||||
user->list->addItem(new tsl::elm::ListItem(Key, Value, colour_unpatched));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}, &callback_userdata, LOG_PATH);
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
frame->setContent(list);
|
||||
return frame;
|
||||
}
|
||||
|
||||
ConfigEntry config_patch_sysmmc{"options", "patch_sysmmc", true};
|
||||
ConfigEntry config_patch_emummc{"options", "patch_emummc", true};
|
||||
ConfigEntry config_logging{"options", "patch_logging", true};
|
||||
};
|
||||
|
||||
// libtesla already initialized fs, hid, pl, pmdmnt, hid:sys and set:sys
|
||||
class SysPatchOverlay final : public tsl::Overlay {
|
||||
public:
|
||||
std::unique_ptr<tsl::Gui> loadInitialGui() override {
|
||||
return initially<GuiMain>();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
return tsl::loop<SysPatchOverlay>(argc, argv);
|
||||
}
|
236
sysmod/Makefile
Normal file
236
sysmod/Makefile
Normal file
@ -0,0 +1,236 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
|
||||
TOPDIR ?= $(CURDIR)
|
||||
include $(DEVKITPRO)/libnx/switch_rules
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data files
|
||||
# INCLUDES is a list of directories containing header files
|
||||
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
|
||||
#
|
||||
# NO_ICON: if set to anything, do not use icon.
|
||||
# NO_NACP: if set to anything, no .nacp file is generated.
|
||||
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
|
||||
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
|
||||
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
|
||||
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
|
||||
# ICON is the filename of the icon (.jpg), relative to the project folder.
|
||||
# If not set, it attempts to use one of the following (in this order):
|
||||
# - <Project name>.jpg
|
||||
# - icon.jpg
|
||||
# - <libnx folder>/default_icon.jpg
|
||||
#
|
||||
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
|
||||
# If not set, it attempts to use one of the following (in this order):
|
||||
# - <Project name>.json
|
||||
# - config.json
|
||||
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
|
||||
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
|
||||
# NACP building is skipped as well.
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := sys-patch
|
||||
BUILD := build
|
||||
SOURCES := src ../common/minIni
|
||||
DATA := data
|
||||
INCLUDES := include ../common
|
||||
#ROMFS := romfs
|
||||
|
||||
# sys-patch
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||
|
||||
CFLAGS := -g -Wall -O2 -ffunction-sections \
|
||||
$(ARCH) $(DEFINES)
|
||||
|
||||
CFLAGS += $(INCLUDE) -D__SWITCH__
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -std=c++23 -fno-rtti -fno-exceptions
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lnx
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
|
||||
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
ifeq ($(strip $(CONFIG_JSON)),)
|
||||
jsons := $(wildcard *.json)
|
||||
ifneq (,$(findstring $(TARGET).json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/$(TARGET).json
|
||||
else
|
||||
ifneq (,$(findstring config.json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/config.json
|
||||
endif
|
||||
endif
|
||||
else
|
||||
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(ICON)),)
|
||||
icons := $(wildcard *.jpg)
|
||||
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
|
||||
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
|
||||
else
|
||||
ifneq (,$(findstring icon.jpg,$(icons)))
|
||||
export APP_ICON := $(TOPDIR)/icon.jpg
|
||||
endif
|
||||
endif
|
||||
else
|
||||
export APP_ICON := $(TOPDIR)/$(ICON)
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(NO_ICON)),)
|
||||
export NROFLAGS += --icon=$(APP_ICON)
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(NO_NACP)),)
|
||||
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
|
||||
endif
|
||||
|
||||
ifneq ($(APP_TITLEID),)
|
||||
export NACPFLAGS += --titleid=$(APP_TITLEID)
|
||||
endif
|
||||
|
||||
ifneq ($(ROMFS),)
|
||||
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
|
||||
endif
|
||||
|
||||
.PHONY: $(BUILD) clean all
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
@rm -rf out/
|
||||
@mkdir -p out/atmosphere/contents/420000000000000B/flags
|
||||
@touch out/atmosphere/contents/420000000000000B/flags/boot2.flag
|
||||
@cp $(CURDIR)/toolbox.json out/atmosphere/contents/420000000000000B/toolbox.json
|
||||
@cp $(CURDIR)/$(TARGET).nsp out/atmosphere/contents/420000000000000B/exefs.nsp
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
ifeq ($(strip $(APP_JSON)),)
|
||||
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
|
||||
else
|
||||
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
|
||||
endif
|
||||
@rm -rf out/
|
||||
@rm -f sys-patch.zip
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
dist: all
|
||||
@echo making dist ...
|
||||
|
||||
@rm -f sys-patch-no-overlay.zip
|
||||
@cd out; zip -r ../sys-patch-no-overlay.zip ./*; cd ../
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(APP_JSON)),)
|
||||
|
||||
all : $(OUTPUT).nro
|
||||
|
||||
ifeq ($(strip $(NO_NACP)),)
|
||||
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
|
||||
else
|
||||
$(OUTPUT).nro : $(OUTPUT).elf
|
||||
endif
|
||||
|
||||
else
|
||||
|
||||
all : $(OUTPUT).nsp
|
||||
|
||||
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
|
||||
|
||||
$(OUTPUT).nso : $(OUTPUT).elf
|
||||
|
||||
endif
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
$(OFILES_SRC) : $(HFILES_BIN)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o %_bin.h : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
@ -2,6 +2,7 @@
|
||||
#include <span>
|
||||
#include <algorithm> // for min
|
||||
#include <bit> // for byteswap
|
||||
#include <utility> // std::unreachable
|
||||
#include <switch.h>
|
||||
#include "minIni/minIni.h"
|
||||
|
||||
@ -69,28 +70,39 @@ struct PatchData {
|
||||
u8 size;
|
||||
};
|
||||
|
||||
enum class PatchedResult {
|
||||
NOT_FOUND,
|
||||
SKIPPED,
|
||||
PATCHED_FILE,
|
||||
PATCHED_SYSPATCH,
|
||||
FAILED_WRITE,
|
||||
};
|
||||
|
||||
struct Patterns {
|
||||
const char* patch_name; // name of patch
|
||||
PatternData byte_pattern; // the pattern to search
|
||||
const PatternData byte_pattern; // the pattern to search
|
||||
|
||||
s32 inst_offset; // instruction offset relative to byte pattern
|
||||
s32 patch_offset; // patch offset relative to inst_offset
|
||||
const s32 inst_offset; // instruction offset relative to byte pattern
|
||||
const s32 patch_offset; // patch offset relative to inst_offset
|
||||
|
||||
bool (*cond)(u32 inst); // check condtion of the instruction
|
||||
PatchData (*patch)(u32 inst); // the patch data to be applied
|
||||
bool (*const cond)(u32 inst); // check condition of the instruction
|
||||
PatchData (*const patch)(u32 inst); // the patch data to be applied
|
||||
bool (*const applied)(u32 inst); // check to see if patch already applied
|
||||
|
||||
u32 min_fw_ver{FW_VER_ANY}; // set to FW_VER_ANY to ignore
|
||||
u32 max_fw_ver{FW_VER_ANY}; // set to FW_VER_ANY to ignore
|
||||
u32 min_ams_ver{FW_VER_ANY}; // set to FW_VER_ANY to ignore
|
||||
u32 max_ams_ver{FW_VER_ANY}; // set to FW_VER_ANY to ignore
|
||||
const u32 min_fw_ver{FW_VER_ANY}; // set to FW_VER_ANY to ignore
|
||||
const u32 max_fw_ver{FW_VER_ANY}; // set to FW_VER_ANY to ignore
|
||||
const u32 min_ams_ver{FW_VER_ANY}; // set to FW_VER_ANY to ignore
|
||||
const u32 max_ams_ver{FW_VER_ANY}; // set to FW_VER_ANY to ignore
|
||||
|
||||
PatchedResult result{PatchedResult::NOT_FOUND};
|
||||
};
|
||||
|
||||
struct PatchEntry {
|
||||
const char* name; // name of the system title
|
||||
u64 title_id; // title id of the system title
|
||||
std::span<const Patterns> patterns; // list of patterns to find
|
||||
u32 min_fw_ver{FW_VER_ANY}; // set to FW_VER_ANY to ignore
|
||||
u32 max_fw_ver{FW_VER_ANY}; // set to FW_VER_ANY to ignore
|
||||
const u64 title_id; // title id of the system title
|
||||
const std::span<Patterns> patterns; // list of patterns to find
|
||||
const u32 min_fw_ver{FW_VER_ANY}; // set to FW_VER_ANY to ignore
|
||||
const u32 max_fw_ver{FW_VER_ANY}; // set to FW_VER_ANY to ignore
|
||||
};
|
||||
|
||||
constexpr auto subi_cond(u32 inst) -> bool {
|
||||
@ -122,15 +134,19 @@ constexpr auto subs_cond(u32 inst) -> bool {
|
||||
constexpr auto cbz_cond(u32 inst) -> bool {
|
||||
const auto type = inst >> 24;
|
||||
return type == 0x34 || type == 0xB4;
|
||||
};
|
||||
}
|
||||
|
||||
constexpr auto mov_cond(u32 inst) -> bool {
|
||||
return ((inst >> 24) & 0x7F) == 0x52;
|
||||
};
|
||||
}
|
||||
|
||||
constexpr auto mov2_cond(u32 inst) -> bool {
|
||||
return (inst >> 24) == 0x2A;
|
||||
};
|
||||
if (hosversionBefore(15,0,0)) {
|
||||
return (inst >> 24) == 0x92; // and x0, x19, #0xffffffff
|
||||
} else {
|
||||
return (inst >> 24) == 0x2A;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr auto bne_cond(u32 inst) -> bool {
|
||||
const auto type = inst >> 24;
|
||||
@ -138,58 +154,77 @@ constexpr auto bne_cond(u32 inst) -> bool {
|
||||
return type == 0x54 || cond == 0x0;
|
||||
}
|
||||
|
||||
// mov w0, wzr (w0 = 0)
|
||||
constexpr auto ret0_patch(u32 inst) -> PatchData {
|
||||
return std::byteswap(0xE0031F2A);
|
||||
return std::byteswap(0xE0031F2AU);
|
||||
}
|
||||
|
||||
// nop
|
||||
constexpr auto nop_patch(u32 inst) -> PatchData {
|
||||
return std::byteswap(0x1F2003D5);
|
||||
return std::byteswap(0x1F2003D5U);
|
||||
}
|
||||
|
||||
constexpr auto subs_patch(u32 inst) -> PatchData {
|
||||
return subi_cond(inst) ? (u8)0x1 : (u8)0x0;
|
||||
}
|
||||
|
||||
// b offset
|
||||
constexpr auto b_patch(u32 inst) -> PatchData {
|
||||
const auto opcode = 0x14;
|
||||
const auto opcode = 0x14 << 24;
|
||||
const auto offset = (inst >> 5) & 0x7FFFF;
|
||||
return opcode | offset;
|
||||
}
|
||||
|
||||
// mov x0, xzr (x0 = 0)
|
||||
constexpr auto mov0_patch(u32 inst) -> PatchData {
|
||||
return std::byteswap(0xE0031FAA);
|
||||
return std::byteswap(0xE0031FAAU);
|
||||
}
|
||||
|
||||
constexpr Patterns fs_patterns[] = {
|
||||
{ "noacidsigchk1", "0xC8FE4739", -24, 0, bl_cond, ret0_patch },
|
||||
{ "noacidsigchk2", "0x0210911F000072", -5, 0, bl_cond, ret0_patch },
|
||||
{ "noncasigchk_old", "0x1E42B9", -5, 0, tbz_cond, nop_patch },
|
||||
{ "noncasigchk_new", "0x3E4479", -5, 0, tbz_cond, nop_patch },
|
||||
{ "nocntchk_old", "0x081C00121F05007181000054", -4, 0, bl_cond, ret0_patch },
|
||||
{ "nocntchk_new", "0x081C00121F05007141010054", -4, 0, bl_cond, ret0_patch },
|
||||
constexpr auto ret0_applied(u32 inst) -> bool {
|
||||
return ret0_patch(inst).data == inst;
|
||||
}
|
||||
|
||||
constexpr auto nop_applied(u32 inst) -> bool {
|
||||
return nop_patch(inst).data == inst;
|
||||
}
|
||||
|
||||
constexpr auto subs_applied(u32 inst) -> bool {
|
||||
const auto type_i = (inst >> 24) & 0xFF;
|
||||
const auto imm = (inst >> 10) & 0xFFF;
|
||||
const auto type_r = (inst >> 21) & 0x7F9;
|
||||
const auto reg = (inst >> 16) & 0x1F;
|
||||
return ((type_i == 0x71) && (imm == 0x1)) || ((type_r == 0x358) && (reg == 0x0));
|
||||
}
|
||||
|
||||
constexpr auto b_applied(u32 inst) -> bool {
|
||||
return 0x14 == (inst >> 24);
|
||||
}
|
||||
|
||||
constexpr auto mov0_applied(u32 inst) -> bool {
|
||||
return mov0_patch(inst).data == inst;
|
||||
}
|
||||
|
||||
constinit Patterns fs_patterns[] = {
|
||||
{ "noacidsigchk1", "0xC8FE4739", -24, 0, bl_cond, ret0_patch, ret0_applied },
|
||||
{ "noacidsigchk2", "0x0210911F000072", -5, 0, bl_cond, ret0_patch, ret0_applied },
|
||||
{ "noncasigchk_old", "0x1E42B9", -5, 0, tbz_cond, nop_patch, nop_applied },
|
||||
{ "noncasigchk_new", "0x3E4479", -5, 0, tbz_cond, nop_patch, nop_applied },
|
||||
{ "nocntchk_old", "0x081C00121F05007181000054", -4, 0, bl_cond, ret0_patch, ret0_applied },
|
||||
{ "nocntchk_new", "0x081C00121F05007141010054", -4, 0, bl_cond, ret0_patch, ret0_applied },
|
||||
};
|
||||
|
||||
constexpr Patterns ldr_patterns[] = {
|
||||
{ "noacidsigchk", "0xFD7BC6A8C0035FD6", 16, 2, subs_cond, subs_patch },
|
||||
constinit Patterns ldr_patterns[] = {
|
||||
{ "noacidsigchk", "0xFD7BC6A8C0035FD6", 16, 2, subs_cond, subs_patch, subs_applied },
|
||||
};
|
||||
|
||||
// todo: make patch for fw 14.0.0 - 14.1.2
|
||||
constexpr Patterns es_patterns[] = {
|
||||
{ "es", "0x1F90013128928052", -4, 0, cbz_cond, b_patch, FW_VER_ANY, MAKEHOSVERSION(13,2,1) },
|
||||
{ "es", "0xC07240F9E1930091", -4, 0, tbz_cond, nop_patch, FW_VER_ANY, MAKEHOSVERSION(10,2,0) },
|
||||
{ "es", "0xF3031FAA02000014", -4, 0, bne_cond, nop_patch, FW_VER_ANY, MAKEHOSVERSION(10,2,0) },
|
||||
{ "es", "0xC0FDFF35A8C35838", -4, 0, mov_cond, nop_patch, MAKEHOSVERSION(11,0,0), MAKEHOSVERSION(13,2,1) },
|
||||
{ "es", "0xE023009145EEFF97", -4, 0, cbz_cond, b_patch, MAKEHOSVERSION(11,0,0), MAKEHOSVERSION(13,2,1) },
|
||||
{ "es", "0x.6300...0094A0..D1..FF97", 16, 0, mov2_cond, mov0_patch, MAKEHOSVERSION(15,0,0) },
|
||||
constinit Patterns es_patterns[] = {
|
||||
{ "es1", "0x1F90013128928052", -4, 0, cbz_cond, b_patch, b_applied, FW_VER_ANY, MAKEHOSVERSION(13,2,1) },
|
||||
{ "es2", "0xC07240F9E1930091", -4, 0, tbz_cond, nop_patch, nop_applied, FW_VER_ANY, MAKEHOSVERSION(10,2,0) },
|
||||
{ "es3", "0xF3031FAA02000014", -4, 0, bne_cond, nop_patch, nop_applied, FW_VER_ANY, MAKEHOSVERSION(10,2,0) },
|
||||
{ "es4", "0xC0FDFF35A8C35838", -4, 0, mov_cond, nop_patch, nop_applied, MAKEHOSVERSION(11,0,0), MAKEHOSVERSION(13,2,1) },
|
||||
{ "es5", "0xE023009145EEFF97", -4, 0, cbz_cond, b_patch, b_applied, MAKEHOSVERSION(11,0,0), MAKEHOSVERSION(13,2,1) },
|
||||
{ "es6", "0x.6300...0094A0..D1..FF97", 16, 0, mov2_cond, mov0_patch, mov0_applied, MAKEHOSVERSION(14,0,0) },
|
||||
};
|
||||
|
||||
// NOTE: add system titles that you want to be patched to this table.
|
||||
// a list of system titles can be found here https://switchbrew.org/wiki/Title_list
|
||||
constexpr PatchEntry patches[] = {
|
||||
constinit PatchEntry patches[] = {
|
||||
{ "fs", 0x0100000000000000, fs_patterns },
|
||||
// ldr needs to be patched in fw 10+
|
||||
{ "ldr", 0x0100000000000001, ldr_patterns, MAKEHOSVERSION(10,0,0) },
|
||||
@ -216,13 +251,19 @@ auto is_emummc() -> bool {
|
||||
return (paths.unk[0] != '\0') || (paths.nintendo[0] != '\0');
|
||||
}
|
||||
|
||||
auto patcher(Handle handle, std::span<const u8> data, u64 addr, std::span<const Patterns> patterns) -> bool {
|
||||
auto patcher(Handle handle, std::span<const u8> data, u64 addr, std::span<Patterns> patterns) -> bool {
|
||||
for (auto& p : patterns) {
|
||||
// skip if version isn't valid
|
||||
if ((p.min_fw_ver && p.min_fw_ver > FW_VERSION) ||
|
||||
(p.max_fw_ver && p.max_fw_ver < FW_VERSION) ||
|
||||
(p.min_ams_ver && p.min_ams_ver > AMS_VERSION) ||
|
||||
(p.max_ams_ver && p.max_ams_ver < AMS_VERSION)) {
|
||||
p.result = PatchedResult::SKIPPED;
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip if already patched
|
||||
if (p.result == PatchedResult::PATCHED_FILE || p.result == PatchedResult::PATCHED_SYSPATCH) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -259,11 +300,16 @@ auto patcher(Handle handle, std::span<const u8> data, u64 addr, std::span<const
|
||||
|
||||
// todo: log failed writes, although this should in theory never fail
|
||||
if (R_FAILED(svcWriteDebugProcessMemory(handle, &patch_data, patch_offset, patch_size))) {
|
||||
p.result = PatchedResult::FAILED_WRITE;
|
||||
} else {
|
||||
// todo: log that this was successful
|
||||
p.result = PatchedResult::PATCHED_SYSPATCH;
|
||||
}
|
||||
|
||||
break; // move onto next pattern
|
||||
// move onto next pattern
|
||||
break;
|
||||
} else if (p.applied(inst)) {
|
||||
// patch already applied by sigpatches
|
||||
p.result = PatchedResult::PATCHED_FILE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -272,7 +318,7 @@ auto patcher(Handle handle, std::span<const u8> data, u64 addr, std::span<const
|
||||
return false;
|
||||
}
|
||||
|
||||
auto apply_patch(const PatchEntry& patch) -> bool {
|
||||
auto apply_patch(PatchEntry& patch) -> bool {
|
||||
Handle handle{};
|
||||
DebugEventInfo event_info{};
|
||||
|
||||
@ -283,6 +329,9 @@ auto apply_patch(const PatchEntry& patch) -> bool {
|
||||
// skip if version isn't valid
|
||||
if ((patch.min_fw_ver && patch.min_fw_ver > FW_VERSION) ||
|
||||
(patch.max_fw_ver && patch.max_fw_ver < FW_VERSION)) {
|
||||
for (auto& p : patch.patterns) {
|
||||
p.result = PatchedResult::SKIPPED;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -318,7 +367,7 @@ auto apply_patch(const PatchEntry& patch) -> bool {
|
||||
const auto actual_size = std::min(READ_BUFFER_SIZE, mem_info.size);
|
||||
if (R_FAILED(svcReadDebugProcessMemory(buffer, handle, mem_info.addr + sz, actual_size))) {
|
||||
// todo: log failed reads!
|
||||
continue;
|
||||
break;
|
||||
} else {
|
||||
patcher(handle, std::span{buffer, actual_size}, mem_info.addr + sz, patch.patterns);
|
||||
}
|
||||
@ -361,29 +410,59 @@ auto ini_load_or_write_default(const char* section, const char* key, long _defau
|
||||
}
|
||||
}
|
||||
|
||||
auto patch_result_to_str(PatchedResult result) -> const char* {
|
||||
switch (result) {
|
||||
case PatchedResult::NOT_FOUND: return "Unpatched";
|
||||
case PatchedResult::SKIPPED: return "Skipped";
|
||||
case PatchedResult::PATCHED_FILE: return "Patched (file)";
|
||||
case PatchedResult::PATCHED_SYSPATCH: return "Patched (sys-patch)";
|
||||
case PatchedResult::FAILED_WRITE: return "Failed (svcWriteDebugProcessMemory)";
|
||||
}
|
||||
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
constexpr auto ini_path = "/config/sys-patch/config.ini";
|
||||
constexpr auto log_path = "/config/sys-patch/log.ini";
|
||||
|
||||
create_dir("/config/");
|
||||
create_dir("/config/sys-patch/");
|
||||
ini_remove(log_path);
|
||||
|
||||
const auto ini_path = "/config/sys-patch/config.ini";
|
||||
const auto patch_sysmmc = ini_load_or_write_default("options", "patch_sysmmc", 1, ini_path);
|
||||
const auto patch_emummc = ini_load_or_write_default("options", "patch_emummc", 1, ini_path);
|
||||
const auto enable_logging = ini_load_or_write_default("options", "enable_logging", 1, ini_path);
|
||||
const auto emummc = is_emummc();
|
||||
bool enable_patching = true;
|
||||
|
||||
// check if we should patch sysmmc
|
||||
if (!patch_sysmmc && !emummc) {
|
||||
return 0;
|
||||
enable_patching = false;
|
||||
}
|
||||
|
||||
// check if we should patch emummc
|
||||
if (!patch_emummc && emummc) {
|
||||
return 0;
|
||||
enable_patching = false;
|
||||
}
|
||||
|
||||
for (auto& patch : patches) {
|
||||
apply_patch(patch);
|
||||
if (enable_patching) {
|
||||
for (auto& patch : patches) {
|
||||
apply_patch(patch);
|
||||
}
|
||||
}
|
||||
|
||||
if (enable_logging) {
|
||||
for (auto& patch : patches) {
|
||||
for (auto& p : patch.patterns) {
|
||||
if (!enable_patching) {
|
||||
p.result = PatchedResult::SKIPPED;
|
||||
}
|
||||
ini_puts(patch.name, p.patch_name, patch_result_to_str(p.result), log_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// note: sysmod exits here.
|
Loading…
Reference in New Issue
Block a user