Lots of changes/new features
- To override the last used boot mode on soft reboot, you only need to press A if you want to boot to the default option. Holding L(+payload button)/R is enough for the other modes. - Added version number to the config menu - Replaced the memsearch algorithm with a faster one - Integrated 3ds_injector from @yifanlu. This brings us region free and all the other FreeMultiPatcher patches. Other than that, you now have the possibility to display the currently booted NAND/FIRM in System Settings! - Rewritten most code for the config menu. You now can navigate to the first/last options with left and right. - You can now choose the 9.0 FIRM to be default in the config menu. This will essentially switch "no buttons" and L in both modes. - You can now choose the second emuNAND to be default in the config menu. This will essentially switch "B is not pressed" and "B is pressed". - When the second emuNAND is booted, it will persist like the other boot options on soft reboot - Bugfixes
This commit is contained in:
parent
e8ebb2f7fe
commit
5f32779ceb
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,6 +1,7 @@
|
|||||||
out
|
out
|
||||||
build
|
build
|
||||||
loader/build
|
loader/build
|
||||||
|
injector/build
|
||||||
*.bin
|
*.bin
|
||||||
*.3dsx
|
*.3dsx
|
||||||
*.smdh
|
*.smdh
|
||||||
|
9
Makefile
9
Makefile
@ -11,6 +11,7 @@ version := $(shell git describe --abbrev=0 --tags)
|
|||||||
dir_source := source
|
dir_source := source
|
||||||
dir_patches := patches
|
dir_patches := patches
|
||||||
dir_loader := loader
|
dir_loader := loader
|
||||||
|
dir_injector := injector
|
||||||
dir_mset := CakeHax
|
dir_mset := CakeHax
|
||||||
dir_ninjhax := CakeBrah
|
dir_ninjhax := CakeBrah
|
||||||
dir_build := build
|
dir_build := build
|
||||||
@ -47,6 +48,7 @@ clean:
|
|||||||
@$(MAKE) $(FLAGS) -C $(dir_ninjhax) clean
|
@$(MAKE) $(FLAGS) -C $(dir_ninjhax) clean
|
||||||
@rm -rf $(dir_out) $(dir_build)
|
@rm -rf $(dir_out) $(dir_build)
|
||||||
@$(MAKE) -C $(dir_loader) clean
|
@$(MAKE) -C $(dir_loader) clean
|
||||||
|
@$(MAKE) -C $(dir_injector) clean
|
||||||
|
|
||||||
$(dir_out):
|
$(dir_out):
|
||||||
@mkdir -p "$(dir_out)/aurei/payloads"
|
@mkdir -p "$(dir_out)/aurei/payloads"
|
||||||
@ -67,12 +69,13 @@ $(dir_out)/3ds/$(name): $(dir_out)
|
|||||||
$(dir_out)/$(name).zip: launcher a9lh ninjhax
|
$(dir_out)/$(name).zip: launcher a9lh ninjhax
|
||||||
@cd $(dir_out) && zip -9 -r $(name) *
|
@cd $(dir_out) && zip -9 -r $(name) *
|
||||||
|
|
||||||
$(dir_build)/patches.h: $(dir_patches)/emunand.s $(dir_patches)/reboot.s
|
$(dir_build)/patches.h: $(dir_patches)/emunand.s $(dir_patches)/reboot.s $(dir_injector)/Makefile
|
||||||
@mkdir -p "$(dir_build)"
|
@mkdir -p "$(dir_build)"
|
||||||
@armips $<
|
@armips $<
|
||||||
@armips $(word 2,$^)
|
@armips $(word 2,$^)
|
||||||
@mv emunand.bin reboot.bin $(dir_build)
|
@$(MAKE) -C $(dir_injector)
|
||||||
@bin2c -o $@ -n emunand $(dir_build)/emunand.bin -n reboot $(dir_build)/reboot.bin
|
@mv emunand.bin reboot.bin $(dir_injector)/injector.cxi $(dir_build)
|
||||||
|
@bin2c -o $@ -n emunand $(dir_build)/emunand.bin -n reboot $(dir_build)/reboot.bin -n injector $(dir_build)/injector.cxi
|
||||||
|
|
||||||
$(dir_build)/loader.h: $(dir_loader)/Makefile
|
$(dir_build)/loader.h: $(dir_loader)/Makefile
|
||||||
@$(MAKE) -C $(dir_loader)
|
@$(MAKE) -C $(dir_loader)
|
||||||
|
21
injector/LICENSE
Normal file
21
injector/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016 Yifan Lu
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
161
injector/Makefile
Normal file
161
injector/Makefile
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
.SUFFIXES:
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ifeq ($(strip $(DEVKITARM)),)
|
||||||
|
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||||
|
endif
|
||||||
|
|
||||||
|
TOPDIR ?= $(CURDIR)
|
||||||
|
MAKEROM ?= makerom
|
||||||
|
include $(DEVKITARM)/3ds_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
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
TARGET := $(notdir $(CURDIR))
|
||||||
|
BUILD := build
|
||||||
|
SOURCES := source
|
||||||
|
DATA := data
|
||||||
|
INCLUDES := include
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# options for code generation
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
|
||||||
|
|
||||||
|
CFLAGS := -flto -Wall -O2 -mword-relocations \
|
||||||
|
-ffast-math -ffunction-sections -fdata-sections \
|
||||||
|
$(ARCH)
|
||||||
|
|
||||||
|
CFLAGS += $(INCLUDE) -DARM11 -D_3DS
|
||||||
|
|
||||||
|
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu99
|
||||||
|
|
||||||
|
ASFLAGS := $(ARCH)
|
||||||
|
LDFLAGS = -flto -Xlinker --defsym="__start__=0x14000000" -specs=3dsx.specs $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||||
|
|
||||||
|
LIBS := -lctru
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# list of directories containing libraries, this must be the top level containing
|
||||||
|
# include and lib
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
LIBDIRS := $(CTRULIB)
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# 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)))
|
||||||
|
PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica)))
|
||||||
|
SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist)))
|
||||||
|
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# use CXX for linking C++ projects, CC for standard C
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifeq ($(strip $(CPPFILES)),)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CC)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CXX)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||||
|
$(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) \
|
||||||
|
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||||
|
|
||||||
|
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||||
|
-I$(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||||
|
|
||||||
|
.PHONY: $(BUILD) clean all
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
all: $(BUILD)
|
||||||
|
|
||||||
|
$(BUILD):
|
||||||
|
@[ -d $@ ] || mkdir -p $@
|
||||||
|
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
clean:
|
||||||
|
@echo clean ...
|
||||||
|
@rm -fr $(BUILD) $(OUTPUT).cxi $(TARGET).elf
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
|
||||||
|
DEPENDS := $(OFILES:.o=.d)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# main targets
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
$(OUTPUT).cxi : $(OUTPUT).elf
|
||||||
|
$(MAKEROM) -f ncch -rsf ../loader.rsf -nocodepadding -o $@ -elf $<
|
||||||
|
|
||||||
|
$(OUTPUT).elf : $(OFILES)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# you need a rule like this for each extension you use as binary data
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.bin.o : %.bin
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@$(bin2o)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# rules for assembling GPU shaders
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
define shader-as
|
||||||
|
$(eval CURBIN := $(patsubst %.shbin.o,%.shbin,$(notdir $@)))
|
||||||
|
picasso -o $(CURBIN) $1
|
||||||
|
bin2s $(CURBIN) | $(AS) -o $@
|
||||||
|
echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h
|
||||||
|
echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h
|
||||||
|
echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h
|
||||||
|
endef
|
||||||
|
|
||||||
|
%.shbin.o : %.v.pica %.g.pica
|
||||||
|
@echo $(notdir $^)
|
||||||
|
@$(call shader-as,$^)
|
||||||
|
|
||||||
|
%.shbin.o : %.v.pica
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@$(call shader-as,$<)
|
||||||
|
|
||||||
|
%.shbin.o : %.shlist
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)/$(file)))
|
||||||
|
|
||||||
|
-include $(DEPENDS)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------------
|
30
injector/README.md
Normal file
30
injector/README.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
3DS Loader Replacement
|
||||||
|
======================
|
||||||
|
|
||||||
|
This is an open source implementation of 3DS `loader` system module--with
|
||||||
|
additional features. The current aim of the project is to provide a nice
|
||||||
|
entry point for patching 3DS modules.
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
Right now, this can serve as an open-source replacement for the built in loader.
|
||||||
|
There is additional support for patching any executable after it's loaded but
|
||||||
|
before it starts. For example, you can patch `menu` to skip region checks and
|
||||||
|
have region free game launching directly from the home menu. There is also
|
||||||
|
support for SDMC reading (not found in original loader implementation) which
|
||||||
|
means that patches can be loaded from the SD card. Ultimately, there would be
|
||||||
|
a patch system that supports easy loading of patches from the SD card.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
You need a working 3DS build environment with a fairly recent copy of devkitARM,
|
||||||
|
ctrulib, and makerom. If you see any errors in the build process, it's likely
|
||||||
|
that you're using an older version.
|
||||||
|
|
||||||
|
Currently, there is no support for FIRM building, so you need to do some steps
|
||||||
|
manually. First, you have to add padding to make sure the NCCH is of the right
|
||||||
|
size to drop in as a replacement. A hacky way is
|
||||||
|
[this patch](http://pastebin.com/nyKXLnNh) which adds junk data. Play around
|
||||||
|
with the size value to get the NCCH to be the exact same size as the one
|
||||||
|
found in your decrypted FIRM dump.
|
||||||
|
|
||||||
|
Once you have a NCCH of the right size, just replace it in your decrypted FIRM
|
||||||
|
and find a way to launch it (for example with ReiNAND).
|
115
injector/loader.rsf
Normal file
115
injector/loader.rsf
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
BasicInfo:
|
||||||
|
Title : loader
|
||||||
|
CompanyCode : "00"
|
||||||
|
ProductCode : 0828builder
|
||||||
|
ContentType : Application
|
||||||
|
Logo : None
|
||||||
|
|
||||||
|
TitleInfo:
|
||||||
|
UniqueId : 0x13
|
||||||
|
Category : Base
|
||||||
|
Version : 2
|
||||||
|
|
||||||
|
Option:
|
||||||
|
UseOnSD : false
|
||||||
|
FreeProductCode : true # Removes limitations on ProductCode
|
||||||
|
MediaFootPadding : false # If true CCI files are created with padding
|
||||||
|
EnableCrypt : false # Enables encryption for NCCH and CIA
|
||||||
|
EnableCompress : true # Compresses exefs code
|
||||||
|
|
||||||
|
AccessControlInfo:
|
||||||
|
IdealProcessor : 1
|
||||||
|
AffinityMask : 3
|
||||||
|
|
||||||
|
Priority : 20
|
||||||
|
|
||||||
|
DisableDebug : true
|
||||||
|
EnableForceDebug : false
|
||||||
|
CanWriteSharedPage : false
|
||||||
|
CanUsePrivilegedPriority : false
|
||||||
|
CanUseNonAlphabetAndNumber : false
|
||||||
|
PermitMainFunctionArgument : false
|
||||||
|
CanShareDeviceMemory : false
|
||||||
|
RunnableOnSleep : true
|
||||||
|
SpecialMemoryArrange : true
|
||||||
|
ResourceLimitCategory : Other
|
||||||
|
|
||||||
|
CoreVersion : 2
|
||||||
|
DescVersion : 2
|
||||||
|
|
||||||
|
MemoryType : Base # Application / System / Base
|
||||||
|
HandleTableSize: 0
|
||||||
|
SystemCallAccess:
|
||||||
|
AcceptSession: 74
|
||||||
|
ArbitrateAddress: 34
|
||||||
|
Break: 60
|
||||||
|
CancelTimer: 28
|
||||||
|
ClearEvent: 25
|
||||||
|
ClearTimer: 29
|
||||||
|
CloseHandle: 35
|
||||||
|
ConnectToPort: 45
|
||||||
|
ControlMemory: 1
|
||||||
|
CreateAddressArbiter: 33
|
||||||
|
CreateCodeSet: 115
|
||||||
|
CreateEvent: 23
|
||||||
|
CreateMemoryBlock: 30
|
||||||
|
CreateMutex: 19
|
||||||
|
CreatePort: 71
|
||||||
|
CreateProcess: 117
|
||||||
|
CreateSemaphore: 21
|
||||||
|
CreateSessionToPort: 72
|
||||||
|
CreateThread: 8
|
||||||
|
CreateTimer: 26
|
||||||
|
DuplicateHandle: 39
|
||||||
|
ExitProcess: 3
|
||||||
|
ExitThread: 9
|
||||||
|
GetCurrentProcessorNumber: 17
|
||||||
|
GetHandleInfo: 41
|
||||||
|
GetProcessId: 53
|
||||||
|
GetProcessIdealProcessor: 6
|
||||||
|
GetProcessIdOfThread: 54
|
||||||
|
GetProcessInfo: 43
|
||||||
|
GetResourceLimit: 56
|
||||||
|
GetResourceLimitCurrentValues: 58
|
||||||
|
GetResourceLimitLimitValues: 57
|
||||||
|
GetSystemInfo: 42
|
||||||
|
GetSystemTick: 40
|
||||||
|
GetThreadContext: 59
|
||||||
|
GetThreadId: 55
|
||||||
|
GetThreadIdealProcessor: 15
|
||||||
|
GetThreadInfo: 44
|
||||||
|
GetThreadPriority: 11
|
||||||
|
MapMemoryBlock: 31
|
||||||
|
OutputDebugString: 61
|
||||||
|
QueryMemory: 2
|
||||||
|
RandomStub: 116
|
||||||
|
ReleaseMutex: 20
|
||||||
|
ReleaseSemaphore: 22
|
||||||
|
ReplyAndReceive1: 75
|
||||||
|
ReplyAndReceive2: 76
|
||||||
|
ReplyAndReceive3: 77
|
||||||
|
ReplyAndReceive4: 78
|
||||||
|
ReplyAndReceive: 79
|
||||||
|
SendSyncRequest1: 46
|
||||||
|
SendSyncRequest2: 47
|
||||||
|
SendSyncRequest3: 48
|
||||||
|
SendSyncRequest4: 49
|
||||||
|
SendSyncRequest: 50
|
||||||
|
SetThreadPriority: 12
|
||||||
|
SetTimer: 27
|
||||||
|
SignalEvent: 24
|
||||||
|
SleepThread: 10
|
||||||
|
UnmapMemoryBlock: 32
|
||||||
|
WaitSynchronization1: 36
|
||||||
|
WaitSynchronizationN: 37
|
||||||
|
InterruptNumbers:
|
||||||
|
ServiceAccessControl:
|
||||||
|
- fs:LDR
|
||||||
|
FileSystemAccess:
|
||||||
|
- DirectSdmc
|
||||||
|
- CtrNandRw
|
||||||
|
|
||||||
|
SystemControlInfo:
|
||||||
|
SaveDataSize: 0KB # It doesn't use any save data.
|
||||||
|
RemasterVersion: 0
|
||||||
|
StackSize: 0x1000
|
97
injector/source/exheader.h
Executable file
97
injector/source/exheader.h
Executable file
@ -0,0 +1,97 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <3ds/types.h>
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
u8 reserved[5];
|
||||||
|
u8 flag;
|
||||||
|
u8 remasterversion[2];
|
||||||
|
} PACKED exheader_systeminfoflags;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
u32 address;
|
||||||
|
u32 nummaxpages;
|
||||||
|
u32 codesize;
|
||||||
|
} PACKED exheader_codesegmentinfo;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
u8 name[8];
|
||||||
|
exheader_systeminfoflags flags;
|
||||||
|
exheader_codesegmentinfo text;
|
||||||
|
u8 stacksize[4];
|
||||||
|
exheader_codesegmentinfo ro;
|
||||||
|
u8 reserved[4];
|
||||||
|
exheader_codesegmentinfo data;
|
||||||
|
u32 bsssize;
|
||||||
|
} PACKED exheader_codesetinfo;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
u64 programid[0x30];
|
||||||
|
} PACKED exheader_dependencylist;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
u8 savedatasize[4];
|
||||||
|
u8 reserved[4];
|
||||||
|
u8 jumpid[8];
|
||||||
|
u8 reserved2[0x30];
|
||||||
|
} PACKED exheader_systeminfo;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
u8 extsavedataid[8];
|
||||||
|
u8 systemsavedataid[8];
|
||||||
|
u8 reserved[8];
|
||||||
|
u8 accessinfo[7];
|
||||||
|
u8 otherattributes;
|
||||||
|
} PACKED exheader_storageinfo;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
u64 programid;
|
||||||
|
u8 flags[8];
|
||||||
|
u16 resourcelimitdescriptor[0x10];
|
||||||
|
exheader_storageinfo storageinfo;
|
||||||
|
u64 serviceaccesscontrol[0x20];
|
||||||
|
u8 reserved[0x1f];
|
||||||
|
u8 resourcelimitcategory;
|
||||||
|
} PACKED exheader_arm11systemlocalcaps;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
u32 descriptors[28];
|
||||||
|
u8 reserved[0x10];
|
||||||
|
} PACKED exheader_arm11kernelcapabilities;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
u8 descriptors[15];
|
||||||
|
u8 descversion;
|
||||||
|
} PACKED exheader_arm9accesscontrol;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// systemcontrol info {
|
||||||
|
// coreinfo {
|
||||||
|
exheader_codesetinfo codesetinfo;
|
||||||
|
exheader_dependencylist deplist;
|
||||||
|
// }
|
||||||
|
exheader_systeminfo systeminfo;
|
||||||
|
// }
|
||||||
|
// accesscontrolinfo {
|
||||||
|
exheader_arm11systemlocalcaps arm11systemlocalcaps;
|
||||||
|
exheader_arm11kernelcapabilities arm11kernelcaps;
|
||||||
|
exheader_arm9accesscontrol arm9accesscontrol;
|
||||||
|
// }
|
||||||
|
struct {
|
||||||
|
u8 signature[0x100];
|
||||||
|
u8 ncchpubkeymodulus[0x100];
|
||||||
|
exheader_arm11systemlocalcaps arm11systemlocalcaps;
|
||||||
|
exheader_arm11kernelcapabilities arm11kernelcaps;
|
||||||
|
exheader_arm9accesscontrol arm9accesscontrol;
|
||||||
|
} PACKED accessdesc;
|
||||||
|
} PACKED exheader_header;
|
109
injector/source/fsldr.c
Normal file
109
injector/source/fsldr.c
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
#include <3ds.h>
|
||||||
|
#include "fsldr.h"
|
||||||
|
#include "fsreg.h"
|
||||||
|
#include "srvsys.h"
|
||||||
|
|
||||||
|
#define SDK_VERSION 0x70200C8
|
||||||
|
|
||||||
|
static Handle fsldrHandle;
|
||||||
|
static int fsldrRefCount;
|
||||||
|
|
||||||
|
// MAKE SURE fsreg has been init before calling this
|
||||||
|
static Result fsldrPatchPermissions(void)
|
||||||
|
{
|
||||||
|
u32 pid;
|
||||||
|
Result res;
|
||||||
|
FS_ProgramInfo info;
|
||||||
|
u32 storage[8] = {0};
|
||||||
|
|
||||||
|
storage[6] = 0x680; // SDMC access and NAND access flag
|
||||||
|
info.programId = 0x0004013000001302LL; // loader PID
|
||||||
|
info.mediaType = MEDIATYPE_NAND;
|
||||||
|
res = svcGetProcessId(&pid, 0xFFFF8001);
|
||||||
|
if (R_SUCCEEDED(res))
|
||||||
|
{
|
||||||
|
res = FSREG_Register(pid, 0xFFFF000000000000LL, &info, (u8 *)storage);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result fsldrInit(void)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
|
||||||
|
if (AtomicPostIncrement(&fsldrRefCount)) return 0;
|
||||||
|
|
||||||
|
ret = srvSysGetServiceHandle(&fsldrHandle, "fs:LDR");
|
||||||
|
if (R_SUCCEEDED(ret))
|
||||||
|
{
|
||||||
|
fsldrPatchPermissions();
|
||||||
|
ret = FSLDR_InitializeWithSdkVersion(fsldrHandle, SDK_VERSION);
|
||||||
|
ret = FSLDR_SetPriority(0);
|
||||||
|
if (R_FAILED(ret)) svcBreak(USERBREAK_ASSERT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AtomicDecrement(&fsldrRefCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsldrExit(void)
|
||||||
|
{
|
||||||
|
if (AtomicDecrement(&fsldrRefCount)) return;
|
||||||
|
svcCloseHandle(fsldrHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FSLDR_InitializeWithSdkVersion(Handle session, u32 version)
|
||||||
|
{
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x861,1,2); // 0x8610042
|
||||||
|
cmdbuf[1] = version;
|
||||||
|
cmdbuf[2] = 32;
|
||||||
|
|
||||||
|
Result ret = 0;
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(session))) return ret;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FSLDR_SetPriority(u32 priority)
|
||||||
|
{
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x862,1,0); // 0x8620040
|
||||||
|
cmdbuf[1] = priority;
|
||||||
|
|
||||||
|
Result ret = 0;
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(fsldrHandle))) return ret;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FSLDR_OpenFileDirectly(Handle* out, FS_Archive archive, FS_Path path, u32 openFlags, u32 attributes)
|
||||||
|
{
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x803,8,4); // 0x8030204
|
||||||
|
cmdbuf[1] = 0;
|
||||||
|
cmdbuf[2] = archive.id;
|
||||||
|
cmdbuf[3] = archive.lowPath.type;
|
||||||
|
cmdbuf[4] = archive.lowPath.size;
|
||||||
|
cmdbuf[5] = path.type;
|
||||||
|
cmdbuf[6] = path.size;
|
||||||
|
cmdbuf[7] = openFlags;
|
||||||
|
cmdbuf[8] = attributes;
|
||||||
|
cmdbuf[9] = IPC_Desc_StaticBuffer(archive.lowPath.size, 2);
|
||||||
|
cmdbuf[10] = (u32) archive.lowPath.data;
|
||||||
|
cmdbuf[11] = IPC_Desc_StaticBuffer(path.size, 0);
|
||||||
|
cmdbuf[12] = (u32) path.data;
|
||||||
|
|
||||||
|
Result ret = 0;
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(fsldrHandle))) return ret;
|
||||||
|
|
||||||
|
if(out) *out = cmdbuf[3];
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
9
injector/source/fsldr.h
Normal file
9
injector/source/fsldr.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <3ds/types.h>
|
||||||
|
|
||||||
|
Result fsldrInit(void);
|
||||||
|
void fsldrExit(void);
|
||||||
|
Result FSLDR_InitializeWithSdkVersion(Handle session, u32 version);
|
||||||
|
Result FSLDR_SetPriority(u32 priority);
|
||||||
|
Result FSLDR_OpenFileDirectly(Handle* out, FS_Archive archive, FS_Path path, u32 openFlags, u32 attributes);
|
116
injector/source/fsreg.c
Normal file
116
injector/source/fsreg.c
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
#include <3ds.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "fsreg.h"
|
||||||
|
#include "srvsys.h"
|
||||||
|
|
||||||
|
static Handle fsregHandle;
|
||||||
|
static int fsregRefCount;
|
||||||
|
|
||||||
|
Result fsregInit(void)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
|
||||||
|
if (AtomicPostIncrement(&fsregRefCount)) return 0;
|
||||||
|
|
||||||
|
ret = srvSysGetServiceHandle(&fsregHandle, "fs:REG");
|
||||||
|
|
||||||
|
if (R_FAILED(ret)) AtomicDecrement(&fsregRefCount);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsregExit(void)
|
||||||
|
{
|
||||||
|
if (AtomicDecrement(&fsregRefCount)) return;
|
||||||
|
svcCloseHandle(fsregHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FSREG_CheckHostLoadId(u64 prog_handle)
|
||||||
|
{
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x406,2,0); // 0x4060080
|
||||||
|
cmdbuf[1] = (u32) (prog_handle);
|
||||||
|
cmdbuf[2] = (u32) (prog_handle >> 32);
|
||||||
|
|
||||||
|
Result ret = 0;
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(fsregHandle))) return ret;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FSREG_LoadProgram(u64 *prog_handle, FS_ProgramInfo *title)
|
||||||
|
{
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x404,4,0); // 0x4040100
|
||||||
|
memcpy(&cmdbuf[1], &title->programId, sizeof(u64));
|
||||||
|
*(u8 *)&cmdbuf[3] = title->mediaType;
|
||||||
|
memcpy(((u8 *)&cmdbuf[3])+1, &title->padding, 7);
|
||||||
|
|
||||||
|
Result ret = 0;
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(fsregHandle))) return ret;
|
||||||
|
*prog_handle = *(u64 *)&cmdbuf[2];
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FSREG_GetProgramInfo(exheader_header *exheader, u32 entry_count, u64 prog_handle)
|
||||||
|
{
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x403,3,0); // 0x40300C0
|
||||||
|
cmdbuf[1] = entry_count;
|
||||||
|
*(u64 *)&cmdbuf[2] = prog_handle;
|
||||||
|
cmdbuf[64] = ((entry_count << 10) << 14) | 2;
|
||||||
|
cmdbuf[65] = (u32) exheader;
|
||||||
|
|
||||||
|
Result ret = 0;
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(fsregHandle))) return ret;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FSREG_UnloadProgram(u64 prog_handle)
|
||||||
|
{
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x405,2,0); // 0x4050080
|
||||||
|
cmdbuf[1] = (u32) (prog_handle);
|
||||||
|
cmdbuf[2] = (u32) (prog_handle >> 32);
|
||||||
|
|
||||||
|
Result ret = 0;
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(fsregHandle))) return ret;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FSREG_Unregister(u32 pid)
|
||||||
|
{
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x402,1,0); // 0x4020040
|
||||||
|
cmdbuf[1] = pid;
|
||||||
|
|
||||||
|
Result ret = 0;
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(fsregHandle))) return ret;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FSREG_Register(u32 pid, u64 prog_handle, FS_ProgramInfo *info, void *storageinfo)
|
||||||
|
{
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x401,0xf,0); // 0x40103C0
|
||||||
|
cmdbuf[1] = pid;
|
||||||
|
*(u64 *)&cmdbuf[2] = prog_handle;
|
||||||
|
memcpy(&cmdbuf[4], &info->programId, sizeof(u64));
|
||||||
|
*(u8 *)&cmdbuf[6] = info->mediaType;
|
||||||
|
memcpy(((u8 *)&cmdbuf[6])+1, &info->padding, 7);
|
||||||
|
memcpy((u8 *)&cmdbuf[8], storageinfo, 32);
|
||||||
|
|
||||||
|
Result ret = 0;
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(fsregHandle))) return ret;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
13
injector/source/fsreg.h
Normal file
13
injector/source/fsreg.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <3ds/types.h>
|
||||||
|
#include "exheader.h"
|
||||||
|
|
||||||
|
Result fsregInit(void);
|
||||||
|
void fsregExit(void);
|
||||||
|
Result FSREG_CheckHostLoadId(u64 prog_handle);
|
||||||
|
Result FSREG_LoadProgram(u64 *prog_handle, FS_ProgramInfo *title);
|
||||||
|
Result FSREG_GetProgramInfo(exheader_header *exheader, u32 entry_count, u64 prog_handle);
|
||||||
|
Result FSREG_UnloadProgram(u64 prog_handle);
|
||||||
|
Result FSREG_Unregister(u32 pid);
|
||||||
|
Result FSREG_Register(u32 pid, u64 prog_handle, FS_ProgramInfo *info, void *storageinfo);
|
105
injector/source/ifile.c
Normal file
105
injector/source/ifile.c
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
#include <3ds.h>
|
||||||
|
#include "ifile.h"
|
||||||
|
#include "fsldr.h"
|
||||||
|
|
||||||
|
Result IFile_Open(IFile *file, FS_Archive archive, FS_Path path, u32 flags)
|
||||||
|
{
|
||||||
|
Result res;
|
||||||
|
|
||||||
|
res = FSLDR_OpenFileDirectly(&file->handle, archive, path, flags, 0);
|
||||||
|
file->pos = 0;
|
||||||
|
file->size = 0;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result IFile_Close(IFile *file)
|
||||||
|
{
|
||||||
|
return FSFILE_Close(file->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result IFile_GetSize(IFile *file, u64 *size)
|
||||||
|
{
|
||||||
|
Result res;
|
||||||
|
|
||||||
|
res = FSFILE_GetSize(file->handle, size);
|
||||||
|
file->size = *size;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result IFile_Read(IFile *file, u64 *total, void *buffer, u32 len)
|
||||||
|
{
|
||||||
|
u32 read;
|
||||||
|
u32 left;
|
||||||
|
char *buf;
|
||||||
|
u64 cur;
|
||||||
|
Result res;
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
{
|
||||||
|
*total = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = (char *)buffer;
|
||||||
|
cur = 0;
|
||||||
|
left = len;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
res = FSFILE_Read(file->handle, &read, file->pos, buf, left);
|
||||||
|
if (R_FAILED(res))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur += read;
|
||||||
|
file->pos += read;
|
||||||
|
if (read == left)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buf += read;
|
||||||
|
left -= read;
|
||||||
|
}
|
||||||
|
|
||||||
|
*total = cur;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result IFile_Write(IFile *file, u64 *total, void *buffer, u32 len, u32 flags)
|
||||||
|
{
|
||||||
|
u32 written;
|
||||||
|
u32 left;
|
||||||
|
char *buf;
|
||||||
|
u64 cur;
|
||||||
|
Result res;
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
{
|
||||||
|
*total = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = (char *)buffer;
|
||||||
|
cur = 0;
|
||||||
|
left = len;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
res = FSFILE_Write(file->handle, &written, file->pos, buf, left, flags);
|
||||||
|
if (R_FAILED(res))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur += written;
|
||||||
|
file->pos += written;
|
||||||
|
if (written == left)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buf += written;
|
||||||
|
left -= written;
|
||||||
|
}
|
||||||
|
|
||||||
|
*total = cur;
|
||||||
|
return res;
|
||||||
|
}
|
16
injector/source/ifile.h
Normal file
16
injector/source/ifile.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <3ds/types.h>
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
Handle handle;
|
||||||
|
u64 pos;
|
||||||
|
u64 size;
|
||||||
|
} IFile;
|
||||||
|
|
||||||
|
Result IFile_Open(IFile *file, FS_Archive archive, FS_Path path, u32 flags);
|
||||||
|
Result IFile_Close(IFile *file);
|
||||||
|
Result IFile_GetSize(IFile *file, u64 *size);
|
||||||
|
Result IFile_Read(IFile *file, u64 *total, void *buffer, u32 len);
|
||||||
|
Result IFile_Write(IFile *file, u64 *total, void *buffer, u32 len, u32 flags);
|
593
injector/source/loader.c
Normal file
593
injector/source/loader.c
Normal file
@ -0,0 +1,593 @@
|
|||||||
|
#include <3ds.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/iosupport.h>
|
||||||
|
#include "patcher.h"
|
||||||
|
#include "exheader.h"
|
||||||
|
#include "ifile.h"
|
||||||
|
#include "fsldr.h"
|
||||||
|
#include "fsreg.h"
|
||||||
|
#include "pxipm.h"
|
||||||
|
#include "srvsys.h"
|
||||||
|
|
||||||
|
#define MAX_SESSIONS 1
|
||||||
|
|
||||||
|
const char CODE_PATH[] = {0x01, 0x00, 0x00, 0x00, 0x2E, 0x63, 0x6F, 0x64, 0x65, 0x00, 0x00, 0x00};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
u32 text_addr;
|
||||||
|
u32 text_size;
|
||||||
|
u32 ro_addr;
|
||||||
|
u32 ro_size;
|
||||||
|
u32 data_addr;
|
||||||
|
u32 data_size;
|
||||||
|
u32 total_size;
|
||||||
|
} prog_addrs_t;
|
||||||
|
|
||||||
|
static Handle g_handles[MAX_SESSIONS+2];
|
||||||
|
static int g_active_handles;
|
||||||
|
static u64 g_cached_prog_handle;
|
||||||
|
static exheader_header g_exheader;
|
||||||
|
static char g_ret_buf[1024];
|
||||||
|
|
||||||
|
static int lzss_decompress(u8 *end)
|
||||||
|
{
|
||||||
|
unsigned int v1; // r1@2
|
||||||
|
u8 *v2; // r2@2
|
||||||
|
u8 *v3; // r3@2
|
||||||
|
u8 *v4; // r1@2
|
||||||
|
char v5; // r5@4
|
||||||
|
char v6; // t1@4
|
||||||
|
signed int v7; // r6@4
|
||||||
|
int v9; // t1@7
|
||||||
|
u8 *v11; // r3@8
|
||||||
|
int v12; // r12@8
|
||||||
|
int v13; // t1@8
|
||||||
|
int v14; // t1@8
|
||||||
|
unsigned int v15; // r7@8
|
||||||
|
int v16; // r12@8
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
if ( end )
|
||||||
|
{
|
||||||
|
v1 = *((u32 *)end - 2);
|
||||||
|
v2 = &end[*((u32 *)end - 1)];
|
||||||
|
v3 = &end[-(v1 >> 24)];
|
||||||
|
v4 = &end[-(v1 & 0xFFFFFF)];
|
||||||
|
while ( v3 > v4 )
|
||||||
|
{
|
||||||
|
v6 = *(v3-- - 1);
|
||||||
|
v5 = v6;
|
||||||
|
v7 = 8;
|
||||||
|
while ( 1 )
|
||||||
|
{
|
||||||
|
if ( (v7-- < 1) )
|
||||||
|
break;
|
||||||
|
if ( v5 & 0x80 )
|
||||||
|
{
|
||||||
|
v13 = *(v3 - 1);
|
||||||
|
v11 = v3 - 1;
|
||||||
|
v12 = v13;
|
||||||
|
v14 = *(v11 - 1);
|
||||||
|
v3 = v11 - 1;
|
||||||
|
v15 = ((v14 | (v12 << 8)) & 0xFFFF0FFF) + 2;
|
||||||
|
v16 = v12 + 32;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ret = v2[v15];
|
||||||
|
*(v2-- - 1) = ret;
|
||||||
|
v16 -= 16;
|
||||||
|
}
|
||||||
|
while ( !(v16 < 0) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
v9 = *(v3-- - 1);
|
||||||
|
ret = v9;
|
||||||
|
*(v2-- - 1) = v9;
|
||||||
|
}
|
||||||
|
v5 *= 2;
|
||||||
|
if ( v3 <= v4 )
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Result allocate_shared_mem(prog_addrs_t *shared, prog_addrs_t *vaddr, int flags)
|
||||||
|
{
|
||||||
|
u32 dummy;
|
||||||
|
|
||||||
|
memcpy(shared, vaddr, sizeof(prog_addrs_t));
|
||||||
|
shared->text_addr = 0x10000000;
|
||||||
|
shared->ro_addr = shared->text_addr + (shared->text_size << 12);
|
||||||
|
shared->data_addr = shared->ro_addr + (shared->ro_size << 12);
|
||||||
|
return svcControlMemory(&dummy, shared->text_addr, 0, shared->total_size << 12, (flags & 0xF00) | MEMOP_ALLOC, MEMPERM_READ | MEMPERM_WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Result load_code(u64 progid, prog_addrs_t *shared, u64 prog_handle, int is_compressed)
|
||||||
|
{
|
||||||
|
IFile file;
|
||||||
|
FS_Archive archive;
|
||||||
|
FS_Path path;
|
||||||
|
Result res;
|
||||||
|
u64 size;
|
||||||
|
u64 total;
|
||||||
|
|
||||||
|
archive.id = ARCHIVE_SAVEDATA_AND_CONTENT2;
|
||||||
|
archive.lowPath.type = PATH_BINARY;
|
||||||
|
archive.lowPath.data = &prog_handle;
|
||||||
|
archive.lowPath.size = 8;
|
||||||
|
//archive.handle = prog_handle; // not needed
|
||||||
|
path.type = PATH_BINARY;
|
||||||
|
path.data = CODE_PATH;
|
||||||
|
path.size = sizeof(CODE_PATH);
|
||||||
|
if (R_FAILED(IFile_Open(&file, archive, path, FS_OPEN_READ)))
|
||||||
|
{
|
||||||
|
svcBreak(USERBREAK_ASSERT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get file size
|
||||||
|
if (R_FAILED(IFile_GetSize(&file, &size)))
|
||||||
|
{
|
||||||
|
IFile_Close(&file);
|
||||||
|
svcBreak(USERBREAK_ASSERT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check size
|
||||||
|
if (size > (u64)shared->total_size << 12)
|
||||||
|
{
|
||||||
|
IFile_Close(&file);
|
||||||
|
return 0xC900464F;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read code
|
||||||
|
res = IFile_Read(&file, &total, (void *)shared->text_addr, size);
|
||||||
|
IFile_Close(&file); // done reading
|
||||||
|
if (R_FAILED(res))
|
||||||
|
{
|
||||||
|
svcBreak(USERBREAK_ASSERT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// decompress
|
||||||
|
if (is_compressed)
|
||||||
|
{
|
||||||
|
lzss_decompress((u8 *)shared->text_addr + size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// patch
|
||||||
|
patch_code(progid, (u8 *)shared->text_addr, shared->total_size << 12);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Result loader_GetProgramInfo(exheader_header *exheader, u64 prog_handle)
|
||||||
|
{
|
||||||
|
Result res;
|
||||||
|
|
||||||
|
if (prog_handle >> 32 == 0xFFFF0000)
|
||||||
|
{
|
||||||
|
return FSREG_GetProgramInfo(exheader, 1, prog_handle);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = FSREG_CheckHostLoadId(prog_handle);
|
||||||
|
//if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32))
|
||||||
|
//so use PXIPM if FSREG fails OR returns "info", is the second condition a bug?
|
||||||
|
if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS))
|
||||||
|
{
|
||||||
|
return PXIPM_GetProgramInfo(exheader, prog_handle);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return FSREG_GetProgramInfo(exheader, 1, prog_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Result loader_LoadProcess(Handle *process, u64 prog_handle)
|
||||||
|
{
|
||||||
|
Result res;
|
||||||
|
int count;
|
||||||
|
u32 flags;
|
||||||
|
u32 desc;
|
||||||
|
u32 dummy;
|
||||||
|
prog_addrs_t shared_addr;
|
||||||
|
prog_addrs_t vaddr;
|
||||||
|
Handle codeset;
|
||||||
|
CodeSetInfo codesetinfo;
|
||||||
|
u32 data_mem_size;
|
||||||
|
u64 progid;
|
||||||
|
|
||||||
|
// make sure the cached info corrosponds to the current prog_handle
|
||||||
|
if (g_cached_prog_handle != prog_handle)
|
||||||
|
{
|
||||||
|
res = loader_GetProgramInfo(&g_exheader, prog_handle);
|
||||||
|
g_cached_prog_handle = prog_handle;
|
||||||
|
if (res < 0)
|
||||||
|
{
|
||||||
|
g_cached_prog_handle = 0;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get kernel flags
|
||||||
|
flags = 0;
|
||||||
|
for (count = 0; count < 28; count++)
|
||||||
|
{
|
||||||
|
desc = g_exheader.arm11kernelcaps.descriptors[count];
|
||||||
|
if (0x1FE == desc >> 23)
|
||||||
|
{
|
||||||
|
flags = desc & 0xF00;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flags == 0)
|
||||||
|
{
|
||||||
|
return MAKERESULT(RL_PERMANENT, RS_INVALIDARG, 1, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate process memory
|
||||||
|
vaddr.text_addr = g_exheader.codesetinfo.text.address;
|
||||||
|
vaddr.text_size = (g_exheader.codesetinfo.text.codesize + 4095) >> 12;
|
||||||
|
vaddr.ro_addr = g_exheader.codesetinfo.ro.address;
|
||||||
|
vaddr.ro_size = (g_exheader.codesetinfo.ro.codesize + 4095) >> 12;
|
||||||
|
vaddr.data_addr = g_exheader.codesetinfo.data.address;
|
||||||
|
vaddr.data_size = (g_exheader.codesetinfo.data.codesize + 4095) >> 12;
|
||||||
|
data_mem_size = (g_exheader.codesetinfo.data.codesize + g_exheader.codesetinfo.bsssize + 4095) >> 12;
|
||||||
|
vaddr.total_size = vaddr.text_size + vaddr.ro_size + vaddr.data_size;
|
||||||
|
if ((res = allocate_shared_mem(&shared_addr, &vaddr, flags)) < 0)
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// load code
|
||||||
|
progid = g_exheader.arm11systemlocalcaps.programid;
|
||||||
|
if ((res = load_code(progid, &shared_addr, prog_handle, g_exheader.codesetinfo.flags.flag & 1)) >= 0)
|
||||||
|
{
|
||||||
|
memcpy(&codesetinfo.name, g_exheader.codesetinfo.name, 8);
|
||||||
|
codesetinfo.program_id = progid;
|
||||||
|
codesetinfo.text_addr = vaddr.text_addr;
|
||||||
|
codesetinfo.text_size = vaddr.text_size;
|
||||||
|
codesetinfo.text_size_total = vaddr.text_size;
|
||||||
|
codesetinfo.ro_addr = vaddr.ro_addr;
|
||||||
|
codesetinfo.ro_size = vaddr.ro_size;
|
||||||
|
codesetinfo.ro_size_total = vaddr.ro_size;
|
||||||
|
codesetinfo.rw_addr = vaddr.data_addr;
|
||||||
|
codesetinfo.rw_size = vaddr.data_size;
|
||||||
|
codesetinfo.rw_size_total = data_mem_size;
|
||||||
|
res = svcCreateCodeSet(&codeset, &codesetinfo, (void *)shared_addr.text_addr, (void *)shared_addr.ro_addr, (void *)shared_addr.data_addr);
|
||||||
|
if (res >= 0)
|
||||||
|
{
|
||||||
|
res = svcCreateProcess(process, codeset, g_exheader.arm11kernelcaps.descriptors, count);
|
||||||
|
svcCloseHandle(codeset);
|
||||||
|
if (res >= 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svcControlMemory(&dummy, shared_addr.text_addr, 0, shared_addr.total_size << 12, MEMOP_FREE, 0);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Result loader_RegisterProgram(u64 *prog_handle, FS_ProgramInfo *title, FS_ProgramInfo *update)
|
||||||
|
{
|
||||||
|
Result res;
|
||||||
|
u64 prog_id;
|
||||||
|
|
||||||
|
prog_id = title->programId;
|
||||||
|
if (prog_id >> 32 != 0xFFFF0000)
|
||||||
|
{
|
||||||
|
res = FSREG_CheckHostLoadId(prog_id);
|
||||||
|
//if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32))
|
||||||
|
if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS))
|
||||||
|
{
|
||||||
|
res = PXIPM_RegisterProgram(prog_handle, title, update);
|
||||||
|
if (res < 0)
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (*prog_handle >> 32 != 0xFFFF0000)
|
||||||
|
{
|
||||||
|
res = FSREG_CheckHostLoadId(*prog_handle);
|
||||||
|
//if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32))
|
||||||
|
if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
svcBreak(USERBREAK_ASSERT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((title->mediaType != update->mediaType) || (prog_id != update->programId))
|
||||||
|
{
|
||||||
|
svcBreak(USERBREAK_ASSERT);
|
||||||
|
}
|
||||||
|
res = FSREG_LoadProgram(prog_handle, title);
|
||||||
|
if (R_SUCCEEDED(res))
|
||||||
|
{
|
||||||
|
if (*prog_handle >> 32 == 0xFFFF0000)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
res = FSREG_CheckHostLoadId(*prog_handle);
|
||||||
|
//if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32))
|
||||||
|
if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS))
|
||||||
|
{
|
||||||
|
svcBreak(USERBREAK_ASSERT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Result loader_UnregisterProgram(u64 prog_handle)
|
||||||
|
{
|
||||||
|
Result res;
|
||||||
|
|
||||||
|
if (prog_handle >> 32 == 0xFFFF0000)
|
||||||
|
{
|
||||||
|
return FSREG_UnloadProgram(prog_handle);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = FSREG_CheckHostLoadId(prog_handle);
|
||||||
|
//if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32))
|
||||||
|
if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS))
|
||||||
|
{
|
||||||
|
return PXIPM_UnregisterProgram(prog_handle);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return FSREG_UnloadProgram(prog_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_commands(void)
|
||||||
|
{
|
||||||
|
FS_ProgramInfo title;
|
||||||
|
FS_ProgramInfo update;
|
||||||
|
u32* cmdbuf;
|
||||||
|
u16 cmdid;
|
||||||
|
int res;
|
||||||
|
Handle handle;
|
||||||
|
u64 prog_handle;
|
||||||
|
|
||||||
|
cmdbuf = getThreadCommandBuffer();
|
||||||
|
cmdid = cmdbuf[0] >> 16;
|
||||||
|
res = 0;
|
||||||
|
switch (cmdid)
|
||||||
|
{
|
||||||
|
case 1: // LoadProcess
|
||||||
|
{
|
||||||
|
res = loader_LoadProcess(&handle, *(u64 *)&cmdbuf[1]);
|
||||||
|
cmdbuf[0] = 0x10042;
|
||||||
|
cmdbuf[1] = res;
|
||||||
|
cmdbuf[2] = 16;
|
||||||
|
cmdbuf[3] = handle;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: // RegisterProgram
|
||||||
|
{
|
||||||
|
memcpy(&title, &cmdbuf[1], sizeof(FS_ProgramInfo));
|
||||||
|
memcpy(&update, &cmdbuf[5], sizeof(FS_ProgramInfo));
|
||||||
|
res = loader_RegisterProgram(&prog_handle, &title, &update);
|
||||||
|
cmdbuf[0] = 0x200C0;
|
||||||
|
cmdbuf[1] = res;
|
||||||
|
*(u64 *)&cmdbuf[2] = prog_handle;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: // UnregisterProgram
|
||||||
|
{
|
||||||
|
if (g_cached_prog_handle == prog_handle)
|
||||||
|
{
|
||||||
|
g_cached_prog_handle = 0;
|
||||||
|
}
|
||||||
|
cmdbuf[0] = 0x30040;
|
||||||
|
cmdbuf[1] = loader_UnregisterProgram(*(u64 *)&cmdbuf[1]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4: // GetProgramInfo
|
||||||
|
{
|
||||||
|
prog_handle = *(u64 *)&cmdbuf[1];
|
||||||
|
if (prog_handle != g_cached_prog_handle)
|
||||||
|
{
|
||||||
|
res = loader_GetProgramInfo(&g_exheader, prog_handle);
|
||||||
|
if (res >= 0)
|
||||||
|
{
|
||||||
|
g_cached_prog_handle = prog_handle;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_cached_prog_handle = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memcpy(&g_ret_buf, &g_exheader, 1024);
|
||||||
|
cmdbuf[0] = 0x40042;
|
||||||
|
cmdbuf[1] = res;
|
||||||
|
cmdbuf[2] = 0x1000002;
|
||||||
|
cmdbuf[3] = (u32) &g_ret_buf;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: // error
|
||||||
|
{
|
||||||
|
cmdbuf[0] = 0x40;
|
||||||
|
cmdbuf[1] = 0xD900182F;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Result should_terminate(int *term_request)
|
||||||
|
{
|
||||||
|
u32 notid;
|
||||||
|
Result ret;
|
||||||
|
|
||||||
|
ret = srvSysReceiveNotification(¬id);
|
||||||
|
if (R_FAILED(ret))
|
||||||
|
{
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (notid == 0x100) // term request
|
||||||
|
{
|
||||||
|
*term_request = 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is called before main
|
||||||
|
void __appInit()
|
||||||
|
{
|
||||||
|
srvSysInit();
|
||||||
|
fsregInit();
|
||||||
|
fsldrInit();
|
||||||
|
pxipmInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is called after main exits
|
||||||
|
void __appExit()
|
||||||
|
{
|
||||||
|
pxipmExit();
|
||||||
|
fsldrExit();
|
||||||
|
fsregExit();
|
||||||
|
srvSysExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// stubs for non-needed pre-main functions
|
||||||
|
void __sync_init();
|
||||||
|
void __sync_fini();
|
||||||
|
void __system_initSyscalls();
|
||||||
|
|
||||||
|
void __ctru_exit(int rc)
|
||||||
|
{
|
||||||
|
__appExit();
|
||||||
|
__sync_fini();
|
||||||
|
svcExitProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void initSystem(void (*retAddr)(void))
|
||||||
|
{
|
||||||
|
__sync_init();
|
||||||
|
__system_initSyscalls();
|
||||||
|
__appInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
Result ret;
|
||||||
|
Handle handle;
|
||||||
|
Handle reply_target;
|
||||||
|
Handle *srv_handle;
|
||||||
|
Handle *notification_handle;
|
||||||
|
s32 index;
|
||||||
|
int i;
|
||||||
|
int term_request;
|
||||||
|
u32* cmdbuf;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
srv_handle = &g_handles[1];
|
||||||
|
notification_handle = &g_handles[0];
|
||||||
|
|
||||||
|
if (R_FAILED(srvSysRegisterService(srv_handle, "Loader", MAX_SESSIONS)))
|
||||||
|
{
|
||||||
|
svcBreak(USERBREAK_ASSERT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (R_FAILED(srvSysEnableNotification(notification_handle)))
|
||||||
|
{
|
||||||
|
svcBreak(USERBREAK_ASSERT);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_active_handles = 2;
|
||||||
|
g_cached_prog_handle = 0;
|
||||||
|
index = 1;
|
||||||
|
|
||||||
|
reply_target = 0;
|
||||||
|
term_request = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (reply_target == 0)
|
||||||
|
{
|
||||||
|
cmdbuf = getThreadCommandBuffer();
|
||||||
|
cmdbuf[0] = 0xFFFF0000;
|
||||||
|
}
|
||||||
|
ret = svcReplyAndReceive(&index, g_handles, g_active_handles, reply_target);
|
||||||
|
|
||||||
|
if (R_FAILED(ret))
|
||||||
|
{
|
||||||
|
// check if any handle has been closed
|
||||||
|
if (ret == 0xC920181A)
|
||||||
|
{
|
||||||
|
if (index == -1)
|
||||||
|
{
|
||||||
|
for (i = 2; i < MAX_SESSIONS+2; i++)
|
||||||
|
{
|
||||||
|
if (g_handles[i] == reply_target)
|
||||||
|
{
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
svcCloseHandle(g_handles[index]);
|
||||||
|
g_handles[index] = g_handles[g_active_handles-1];
|
||||||
|
g_active_handles--;
|
||||||
|
reply_target = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
svcBreak(USERBREAK_ASSERT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// process responses
|
||||||
|
reply_target = 0;
|
||||||
|
switch (index)
|
||||||
|
{
|
||||||
|
case 0: // notification
|
||||||
|
{
|
||||||
|
if (R_FAILED(should_terminate(&term_request)))
|
||||||
|
{
|
||||||
|
svcBreak(USERBREAK_ASSERT);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: // new session
|
||||||
|
{
|
||||||
|
if (R_FAILED(svcAcceptSession(&handle, *srv_handle)))
|
||||||
|
{
|
||||||
|
svcBreak(USERBREAK_ASSERT);
|
||||||
|
}
|
||||||
|
if (g_active_handles < MAX_SESSIONS+2)
|
||||||
|
{
|
||||||
|
g_handles[g_active_handles] = handle;
|
||||||
|
g_active_handles++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
svcCloseHandle(handle);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: // session
|
||||||
|
{
|
||||||
|
handle_commands();
|
||||||
|
reply_target = g_handles[index];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (!term_request || g_active_handles != 2);
|
||||||
|
|
||||||
|
srvSysUnregisterService("Loader");
|
||||||
|
svcCloseHandle(*srv_handle);
|
||||||
|
svcCloseHandle(*notification_handle);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
274
injector/source/patcher.c
Normal file
274
injector/source/patcher.c
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
#include <3ds.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "patcher.h"
|
||||||
|
#include "ifile.h"
|
||||||
|
|
||||||
|
#ifndef PATH_MAX
|
||||||
|
#define PATH_MAX 255
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static u32 config = 0;
|
||||||
|
static u8 secureinfo[0x111] = {0};
|
||||||
|
|
||||||
|
//Quick Search algorithm, adapted from http://igm.univ-mlv.fr/~lecroq/string/node19.html#SECTION00190
|
||||||
|
static u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize){
|
||||||
|
const u8 *patternc = (const u8 *)pattern;
|
||||||
|
|
||||||
|
//Preprocessing
|
||||||
|
int table[256];
|
||||||
|
|
||||||
|
for(u32 i = 0; i < 256; ++i)
|
||||||
|
table[i] = patternSize + 1;
|
||||||
|
for(u32 i = 0; i < patternSize; ++i)
|
||||||
|
table[patternc[i]] = patternSize - i;
|
||||||
|
|
||||||
|
//Searching
|
||||||
|
u32 j = 0;
|
||||||
|
|
||||||
|
while(j <= size - patternSize){
|
||||||
|
if(memcmp(patternc, startPos + j, patternSize) == 0)
|
||||||
|
return startPos + j;
|
||||||
|
j += table[startPos[j + patternSize]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 patch_memory(u8 *start, u32 size, const void *pattern, u32 patsize, int offset, const void *replace, u32 repsize, u32 count){
|
||||||
|
u32 i;
|
||||||
|
|
||||||
|
for(i = 0; i < count; i++){
|
||||||
|
u8 *found = memsearch(start, pattern, size, patsize);
|
||||||
|
if(found == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
memcpy(found + offset, replace, repsize);
|
||||||
|
|
||||||
|
u32 at = (u32)(found - start);
|
||||||
|
|
||||||
|
if(at + patsize > size) size = 0;
|
||||||
|
else size = size - (at + patsize);
|
||||||
|
|
||||||
|
start = found + patsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int file_open(IFile *file, FS_ArchiveID id, const char *path, int flags){
|
||||||
|
FS_Archive archive;
|
||||||
|
FS_Path ppath;
|
||||||
|
|
||||||
|
size_t len = strnlen(path, PATH_MAX);
|
||||||
|
archive.id = id;
|
||||||
|
archive.lowPath.type = PATH_EMPTY;
|
||||||
|
archive.lowPath.size = 1;
|
||||||
|
archive.lowPath.data = (u8 *)"";
|
||||||
|
ppath.type = PATH_ASCII;
|
||||||
|
ppath.data = path;
|
||||||
|
ppath.size = len+1;
|
||||||
|
return IFile_Open(file, archive, ppath, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int patch_secureinfo(){
|
||||||
|
IFile file;
|
||||||
|
Result ret;
|
||||||
|
u64 total;
|
||||||
|
|
||||||
|
if(secureinfo[0] == 0xFF)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = file_open(&file, ARCHIVE_SDMC, "/SecureInfo_A", FS_OPEN_READ);
|
||||||
|
if(R_SUCCEEDED(ret)){
|
||||||
|
ret = IFile_Read(&file, &total, secureinfo, sizeof(secureinfo));
|
||||||
|
IFile_Close(&file);
|
||||||
|
if(R_SUCCEEDED(ret) && total == sizeof(secureinfo)){
|
||||||
|
ret = file_open(&file, ARCHIVE_NAND_RW, "/sys/SecureInfo_C", FS_OPEN_WRITE | FS_OPEN_CREATE);
|
||||||
|
if(R_SUCCEEDED(ret)){
|
||||||
|
ret = IFile_Write(&file, &total, secureinfo, sizeof(secureinfo), FS_WRITE_FLUSH);
|
||||||
|
IFile_Close(&file);
|
||||||
|
}
|
||||||
|
secureinfo[0] = 0xFF; // we repurpose this byte as status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else { // get file from NAND
|
||||||
|
ret = file_open(&file, ARCHIVE_NAND_RW, "/sys/SecureInfo_C", FS_OPEN_READ);
|
||||||
|
if(R_SUCCEEDED(ret)){
|
||||||
|
ret = IFile_Read(&file, &total, secureinfo, sizeof(secureinfo));
|
||||||
|
IFile_Close(&file);
|
||||||
|
if(R_SUCCEEDED(ret) && total == sizeof(secureinfo))
|
||||||
|
secureinfo[0] = 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int open_config(){
|
||||||
|
IFile file;
|
||||||
|
Result ret;
|
||||||
|
u64 total;
|
||||||
|
|
||||||
|
if(config)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = file_open(&file, ARCHIVE_SDMC, "/aurei/config.bin", FS_OPEN_READ);
|
||||||
|
if(R_SUCCEEDED(ret)){
|
||||||
|
ret = IFile_Read(&file, &total, (void *)&config, 3);
|
||||||
|
IFile_Close(&file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patch_code(u64 progid, u8 *code, u32 size){
|
||||||
|
if( progid == 0x0004003000008F02LL || // USA Menu
|
||||||
|
progid == 0x0004003000008202LL || // JPN Menu
|
||||||
|
progid == 0x0004003000009802LL || // EUR Menu
|
||||||
|
progid == 0x000400300000A102LL || // CHN Menu
|
||||||
|
progid == 0x000400300000A902LL || // KOR Menu
|
||||||
|
progid == 0x000400300000B102LL // TWN Menu
|
||||||
|
){
|
||||||
|
static const u8 regionFreePattern[] = {
|
||||||
|
0x00, 0x00, 0x55, 0xE3, 0x01, 0x10, 0xA0, 0xE3
|
||||||
|
};
|
||||||
|
static const u8 regionFreePatch[] = {
|
||||||
|
0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1
|
||||||
|
};
|
||||||
|
|
||||||
|
patch_memory(code, size,
|
||||||
|
regionFreePattern,
|
||||||
|
sizeof(regionFreePattern), -16,
|
||||||
|
regionFreePatch,
|
||||||
|
sizeof(regionFreePatch), 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if(progid == 0x0004013000002C02LL){ // NIM
|
||||||
|
static const u8 blockAutoUpdatesPattern[] = {
|
||||||
|
0x25, 0x79, 0x0B, 0x99
|
||||||
|
};
|
||||||
|
static const u8 blockAutoUpdatesPatch[] = {
|
||||||
|
0xE3, 0xA0
|
||||||
|
};
|
||||||
|
static const u8 blockEShopUpdateCheckPattern[] = {
|
||||||
|
0x30, 0xB5, 0xF1, 0xB0
|
||||||
|
};
|
||||||
|
static const u8 blockEShopUpdateCheckPatch[] = {
|
||||||
|
0x00, 0x20, 0x08, 0x60, 0x70, 0x47
|
||||||
|
};
|
||||||
|
static const u8 countryRespPattern[] = {
|
||||||
|
0x01, 0x20, 0x01, 0x90, 0x22, 0x46, 0x06, 0x9B
|
||||||
|
};
|
||||||
|
static const char countryRespPatchModel[] = {
|
||||||
|
0x06, 0x9A, 0x03, 0x20, 0x90, 0x47, 0x55, 0x21, 0x01, 0x70, 0x53, 0x21, 0x41, 0x70, 0x00, 0x21,
|
||||||
|
0x81, 0x70, 0x60, 0x61, 0x00, 0x20
|
||||||
|
};
|
||||||
|
const char *country;
|
||||||
|
char countryRespPatch[sizeof(countryRespPatchModel)];
|
||||||
|
|
||||||
|
patch_memory(code, size,
|
||||||
|
blockAutoUpdatesPattern,
|
||||||
|
sizeof(blockAutoUpdatesPattern), 0,
|
||||||
|
blockAutoUpdatesPatch,
|
||||||
|
sizeof(blockAutoUpdatesPatch), 1
|
||||||
|
);
|
||||||
|
patch_memory(code, size,
|
||||||
|
blockEShopUpdateCheckPattern,
|
||||||
|
sizeof(blockEShopUpdateCheckPattern), 0,
|
||||||
|
blockEShopUpdateCheckPatch,
|
||||||
|
sizeof(blockEShopUpdateCheckPatch), 1
|
||||||
|
);
|
||||||
|
if(R_SUCCEEDED(patch_secureinfo())){
|
||||||
|
switch(secureinfo[0x100]){
|
||||||
|
case 1: country = "US"; break;
|
||||||
|
case 2: country = "GB"; break; // sorry rest-of-Europe, you have to change this
|
||||||
|
case 3: country = "AU"; break;
|
||||||
|
case 4: country = "CN"; break;
|
||||||
|
case 5: country = "KR"; break;
|
||||||
|
case 6: country = "TW"; break;
|
||||||
|
default: case 0: country = "JP"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// patch XML response Country
|
||||||
|
memcpy(countryRespPatch,
|
||||||
|
countryRespPatchModel,
|
||||||
|
sizeof(countryRespPatchModel)
|
||||||
|
);
|
||||||
|
countryRespPatch[6] = country[0];
|
||||||
|
countryRespPatch[10] = country[1];
|
||||||
|
patch_memory(code, size,
|
||||||
|
countryRespPattern,
|
||||||
|
sizeof(countryRespPattern), 0,
|
||||||
|
countryRespPatch,
|
||||||
|
sizeof(countryRespPatch), 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(
|
||||||
|
progid == 0x0004001000021000LL || // USA MSET
|
||||||
|
progid == 0x0004001000020000LL || // JPN MSET
|
||||||
|
progid == 0x0004001000022000LL || // EUR MSET
|
||||||
|
progid == 0x0004001000026000LL || // CHN MSET
|
||||||
|
progid == 0x0004001000027000LL || // KOR MSET
|
||||||
|
progid == 0x0004001000028000LL // TWN MSET
|
||||||
|
){
|
||||||
|
if(R_SUCCEEDED(open_config()) && ((config >> 5) & 0x1)){
|
||||||
|
static const u16 VerPattern[] = u"Ver.";
|
||||||
|
const u32 currentFirm = ((config >> 12) & 0x1);
|
||||||
|
const u32 currentNand = ((config >> 13) & 0x3);
|
||||||
|
|
||||||
|
patch_memory(code, size,
|
||||||
|
VerPattern,
|
||||||
|
sizeof(VerPattern) - sizeof(u16), 0,
|
||||||
|
currentNand ? ((currentNand == 1) ? ((currentFirm == 1) ? u" Emu" : u"Emu9") : u"Emu2") :
|
||||||
|
((currentFirm == 1) ? u" Sys" : u"Sys9"),
|
||||||
|
sizeof(VerPattern) - sizeof(u16), 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (progid == 0x0004013000008002LL){ // NS
|
||||||
|
static const u8 stopCartUpdatesPattern[] = {
|
||||||
|
0x0C, 0x18, 0xE1, 0xD8
|
||||||
|
};
|
||||||
|
static const u8 stopCartUpdatesPatch[] = {
|
||||||
|
0x0B, 0x18, 0x21, 0xC8
|
||||||
|
};
|
||||||
|
|
||||||
|
patch_memory(code, size,
|
||||||
|
stopCartUpdatesPattern,
|
||||||
|
sizeof(stopCartUpdatesPattern), 0,
|
||||||
|
stopCartUpdatesPatch,
|
||||||
|
sizeof(stopCartUpdatesPatch), 2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if(progid == 0x0004013000001702LL){ // CFG
|
||||||
|
static const u8 secureinfoSigCheckPattern[] = {
|
||||||
|
0x06, 0x46, 0x10, 0x48, 0xFC
|
||||||
|
};
|
||||||
|
static const u8 secureinfoSigCheckPatch[] = {
|
||||||
|
0x00, 0x26
|
||||||
|
};
|
||||||
|
static const u16 secureinfoFilenamePattern[] = u"SecureInfo_";
|
||||||
|
static const u16 secureinfoFilenamePatch[] = u"C";
|
||||||
|
|
||||||
|
// disable SecureInfo signature check
|
||||||
|
patch_memory(code, size,
|
||||||
|
secureinfoSigCheckPattern,
|
||||||
|
sizeof(secureinfoSigCheckPattern), 0,
|
||||||
|
secureinfoSigCheckPatch,
|
||||||
|
sizeof(secureinfoSigCheckPatch), 1
|
||||||
|
);
|
||||||
|
if(R_SUCCEEDED(patch_secureinfo())){
|
||||||
|
// use SecureInfo_C
|
||||||
|
patch_memory(code, size,
|
||||||
|
secureinfoFilenamePattern,
|
||||||
|
sizeof(secureinfoFilenamePattern) - sizeof(u16),
|
||||||
|
sizeof(secureinfoFilenamePattern) - sizeof(u16),
|
||||||
|
secureinfoFilenamePatch,
|
||||||
|
sizeof(secureinfoFilenamePatch) - sizeof(u16), 2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
5
injector/source/patcher.h
Normal file
5
injector/source/patcher.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <3ds/types.h>
|
||||||
|
|
||||||
|
u32 patch_code(u64 progid, u8 *code, u32 size);
|
74
injector/source/pxipm.c
Normal file
74
injector/source/pxipm.c
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
#include <3ds.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "pxipm.h"
|
||||||
|
#include "srvsys.h"
|
||||||
|
|
||||||
|
static Handle pxipmHandle;
|
||||||
|
static int pxipmRefCount;
|
||||||
|
|
||||||
|
Result pxipmInit(void)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
|
||||||
|
if (AtomicPostIncrement(&pxipmRefCount)) return 0;
|
||||||
|
|
||||||
|
ret = srvSysGetServiceHandle(&pxipmHandle, "PxiPM");
|
||||||
|
|
||||||
|
if (R_FAILED(ret)) AtomicDecrement(&pxipmRefCount);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxipmExit(void)
|
||||||
|
{
|
||||||
|
if (AtomicDecrement(&pxipmRefCount)) return;
|
||||||
|
svcCloseHandle(pxipmHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result PXIPM_RegisterProgram(u64 *prog_handle, FS_ProgramInfo *title, FS_ProgramInfo *update)
|
||||||
|
{
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x2,8,0); // 0x20200
|
||||||
|
memcpy(&cmdbuf[1], &title->programId, sizeof(u64));
|
||||||
|
*(u8 *)&cmdbuf[3] = title->mediaType;
|
||||||
|
memcpy(((u8 *)&cmdbuf[3])+1, &title->padding, 7);
|
||||||
|
memcpy(&cmdbuf[5], &update->programId, sizeof(u64));
|
||||||
|
*(u8 *)&cmdbuf[7] = update->mediaType;
|
||||||
|
memcpy(((u8 *)&cmdbuf[7])+1, &update->padding, 7);
|
||||||
|
|
||||||
|
Result ret = 0;
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(pxipmHandle))) return ret;
|
||||||
|
*prog_handle = *(u64*)&cmdbuf[2];
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result PXIPM_GetProgramInfo(exheader_header *exheader, u64 prog_handle)
|
||||||
|
{
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x1,2,2); // 0x10082
|
||||||
|
cmdbuf[1] = (u32) (prog_handle);
|
||||||
|
cmdbuf[2] = (u32) (prog_handle >> 32);
|
||||||
|
cmdbuf[3] = (0x400 << 8) | 0x4;
|
||||||
|
cmdbuf[4] = (u32) exheader;
|
||||||
|
|
||||||
|
Result ret = 0;
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(pxipmHandle))) return ret;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result PXIPM_UnregisterProgram(u64 prog_handle)
|
||||||
|
{
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x3,2,0); // 0x30080
|
||||||
|
cmdbuf[1] = (u32) (prog_handle);
|
||||||
|
cmdbuf[2] = (u32) (prog_handle >> 32);
|
||||||
|
|
||||||
|
Result ret = 0;
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(pxipmHandle))) return ret;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
10
injector/source/pxipm.h
Normal file
10
injector/source/pxipm.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <3ds/types.h>
|
||||||
|
#include "exheader.h"
|
||||||
|
|
||||||
|
Result pxipmInit(void);
|
||||||
|
void pxipmExit(void);
|
||||||
|
Result PXIPM_RegisterProgram(u64 *prog_handle, FS_ProgramInfo *title, FS_ProgramInfo *update);
|
||||||
|
Result PXIPM_GetProgramInfo(exheader_header *exheader, u64 prog_handle);
|
||||||
|
Result PXIPM_UnregisterProgram(u64 prog_handle);
|
154
injector/source/srvsys.c
Normal file
154
injector/source/srvsys.c
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
#include <3ds.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "srvsys.h"
|
||||||
|
|
||||||
|
static Handle srvHandle;
|
||||||
|
static int srvRefCount;
|
||||||
|
static RecursiveLock initLock;
|
||||||
|
static int initLockinit = 0;
|
||||||
|
|
||||||
|
Result srvSysInit()
|
||||||
|
{
|
||||||
|
Result rc = 0;
|
||||||
|
|
||||||
|
if (!initLockinit)
|
||||||
|
{
|
||||||
|
RecursiveLock_Init(&initLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
RecursiveLock_Lock(&initLock);
|
||||||
|
|
||||||
|
if (srvRefCount > 0)
|
||||||
|
{
|
||||||
|
RecursiveLock_Unlock(&initLock);
|
||||||
|
return MAKERESULT(RL_INFO, RS_NOP, 25, RD_ALREADY_INITIALIZED);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
rc = svcConnectToPort(&srvHandle, "srv:");
|
||||||
|
if (R_LEVEL(rc) != RL_PERMANENT ||
|
||||||
|
R_SUMMARY(rc) != RS_NOTFOUND ||
|
||||||
|
R_DESCRIPTION(rc) != RD_NOT_FOUND
|
||||||
|
) break;
|
||||||
|
svcSleepThread(500000);
|
||||||
|
}
|
||||||
|
if (R_SUCCEEDED(rc))
|
||||||
|
{
|
||||||
|
rc = srvSysRegisterClient();
|
||||||
|
srvRefCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecursiveLock_Unlock(&initLock);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result srvSysRegisterClient(void)
|
||||||
|
{
|
||||||
|
Result rc = 0;
|
||||||
|
u32* cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x1,0,2); // 0x10002
|
||||||
|
cmdbuf[1] = IPC_Desc_CurProcessHandle();
|
||||||
|
|
||||||
|
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result srvSysExit()
|
||||||
|
{
|
||||||
|
Result rc;
|
||||||
|
RecursiveLock_Lock(&initLock);
|
||||||
|
|
||||||
|
if (srvRefCount > 1)
|
||||||
|
{
|
||||||
|
srvRefCount--;
|
||||||
|
RecursiveLock_Unlock(&initLock);
|
||||||
|
return MAKERESULT(RL_INFO, RS_NOP, 25, RD_BUSY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srvHandle != 0) svcCloseHandle(srvHandle);
|
||||||
|
else svcBreak(USERBREAK_ASSERT);
|
||||||
|
rc = (Result)srvHandle; // yeah, I think this is a benign bug
|
||||||
|
srvHandle = 0;
|
||||||
|
srvRefCount--;
|
||||||
|
RecursiveLock_Unlock(&initLock);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result srvSysGetServiceHandle(Handle* out, const char* name)
|
||||||
|
{
|
||||||
|
Result rc = 0;
|
||||||
|
u32* cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x5,4,0); // 0x50100
|
||||||
|
strncpy((char*) &cmdbuf[1], name,8);
|
||||||
|
cmdbuf[3] = strlen(name);
|
||||||
|
cmdbuf[4] = 0x0;
|
||||||
|
|
||||||
|
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
||||||
|
|
||||||
|
if(out) *out = cmdbuf[3];
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result srvSysEnableNotification(Handle* semaphoreOut)
|
||||||
|
{
|
||||||
|
Result rc = 0;
|
||||||
|
u32* cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x2,0,0);
|
||||||
|
|
||||||
|
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
||||||
|
|
||||||
|
if(semaphoreOut) *semaphoreOut = cmdbuf[3];
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result srvSysReceiveNotification(u32* notificationIdOut)
|
||||||
|
{
|
||||||
|
Result rc = 0;
|
||||||
|
u32* cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0xB,0,0); // 0xB0000
|
||||||
|
|
||||||
|
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
||||||
|
|
||||||
|
if(notificationIdOut) *notificationIdOut = cmdbuf[2];
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result srvSysRegisterService(Handle* out, const char* name, int maxSessions)
|
||||||
|
{
|
||||||
|
Result rc = 0;
|
||||||
|
u32* cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x3,4,0); // 0x30100
|
||||||
|
strncpy((char*) &cmdbuf[1], name,8);
|
||||||
|
cmdbuf[3] = strlen(name);
|
||||||
|
cmdbuf[4] = maxSessions;
|
||||||
|
|
||||||
|
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
||||||
|
|
||||||
|
if(out) *out = cmdbuf[3];
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result srvSysUnregisterService(const char* name)
|
||||||
|
{
|
||||||
|
Result rc = 0;
|
||||||
|
u32* cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x4,3,0); // 0x400C0
|
||||||
|
strncpy((char*) &cmdbuf[1], name,8);
|
||||||
|
cmdbuf[3] = strlen(name);
|
||||||
|
|
||||||
|
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
||||||
|
|
||||||
|
return cmdbuf[1];
|
||||||
|
}
|
47
injector/source/srvsys.h
Normal file
47
injector/source/srvsys.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* @file srv.h
|
||||||
|
* @brief Service API.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/// Initializes the service API.
|
||||||
|
Result srvSysInit(void);
|
||||||
|
|
||||||
|
/// Exits the service API.
|
||||||
|
Result srvSysExit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retrieves a service handle, retrieving from the environment handle list if possible.
|
||||||
|
* @param out Pointer to write the handle to.
|
||||||
|
* @param name Name of the service.
|
||||||
|
*/
|
||||||
|
Result srvSysGetServiceHandle(Handle* out, const char* name);
|
||||||
|
|
||||||
|
/// Registers the current process as a client to the service API.
|
||||||
|
Result srvSysRegisterClient(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enables service notificatios, returning a notification semaphore.
|
||||||
|
* @param semaphoreOut Pointer to output the notification semaphore to.
|
||||||
|
*/
|
||||||
|
Result srvSysEnableNotification(Handle* semaphoreOut);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Receives a notification.
|
||||||
|
* @param notificationIdOut Pointer to output the ID of the received notification to.
|
||||||
|
*/
|
||||||
|
Result srvSysReceiveNotification(u32* notificationIdOut);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Registers the current process as a service.
|
||||||
|
* @param out Pointer to write the service handle to.
|
||||||
|
* @param name Name of the service.
|
||||||
|
* @param maxSessions Maximum number of sessions the service can handle.
|
||||||
|
*/
|
||||||
|
Result srvSysRegisterService(Handle* out, const char* name, int maxSessions);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Unregisters the current process as a service.
|
||||||
|
* @param name Name of the service.
|
||||||
|
*/
|
||||||
|
Result srvSysUnregisterService(const char* name);
|
@ -13,10 +13,14 @@
|
|||||||
#define BUTTON_L1 (1 << 9)
|
#define BUTTON_L1 (1 << 9)
|
||||||
#define BUTTON_A 1
|
#define BUTTON_A 1
|
||||||
#define BUTTON_B (1 << 1)
|
#define BUTTON_B (1 << 1)
|
||||||
|
#define BUTTON_SELECT (1 << 2)
|
||||||
|
#define BUTTON_START (1 << 3)
|
||||||
|
#define BUTTON_RIGHT (1 << 4)
|
||||||
|
#define BUTTON_LEFT (1 << 5)
|
||||||
#define BUTTON_UP (1 << 6)
|
#define BUTTON_UP (1 << 6)
|
||||||
#define BUTTON_DOWN (1 << 7)
|
#define BUTTON_DOWN (1 << 7)
|
||||||
#define BUTTON_START (1 << 3)
|
#define BUTTON_L1R1 ((1 << 8) | (1 << 9))
|
||||||
#define BUTTON_SELECT (1 << 2)
|
#define SAFE_MODE (BUTTON_L1R1 | BUTTON_A | BUTTON_UP)
|
||||||
#define SAFE_MODE (BUTTON_R1 | BUTTON_L1 | BUTTON_A | BUTTON_UP)
|
#define OPTION_BUTTONS (BUTTON_L1R1 | BUTTON_A)
|
||||||
#define OPTION_BUTTONS (BUTTON_R1 | BUTTON_L1 | BUTTON_A)
|
#define PAYLOAD_BUTTONS ((BUTTON_L1 | BUTTON_A) ^ 0xFFF)
|
||||||
#define PAYLOAD_BUTTONS ((BUTTON_L1 | BUTTON_A) ^ 0xFFF)
|
#define MENU_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_A | BUTTON_START)
|
@ -42,41 +42,41 @@ void loadSplash(void){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawCharacter(char character, int pos_x, int pos_y, u32 color){
|
void drawCharacter(char character, int posX, int posY, u32 color){
|
||||||
u8 *const select = fb->top_left;
|
u8 *const select = fb->top_left;
|
||||||
|
|
||||||
for(int y = 0; y < 8; y++){
|
for(int y = 0; y < 8; y++){
|
||||||
unsigned char char_pos = font[character * 8 + y];
|
char charPos = font[character * 8 + y];
|
||||||
|
|
||||||
for(int x = 7; x >= 0; x--){
|
for(int x = 7; x >= 0; x--){
|
||||||
int screen_pos = (pos_x * SCREEN_TOP_HEIGHT * 3 + (SCREEN_TOP_HEIGHT - y - pos_y - 1) * 3) + (7 - x) * 3 * SCREEN_TOP_HEIGHT;
|
int screenPos = (posX * SCREEN_TOP_HEIGHT * 3 + (SCREEN_TOP_HEIGHT - y - posY - 1) * 3) + (7 - x) * 3 * SCREEN_TOP_HEIGHT;
|
||||||
|
|
||||||
if ((char_pos >> x) & 1) {
|
if ((charPos >> x) & 1) {
|
||||||
select[screen_pos] = color >> 16;
|
select[screenPos] = color >> 16;
|
||||||
select[screen_pos + 1] = color >> 8;
|
select[screenPos + 1] = color >> 8;
|
||||||
select[screen_pos + 2] = color;
|
select[screenPos + 2] = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int drawString(const char *string, int pos_x, int pos_y, u32 color){
|
int drawString(const char *string, int posX, int posY, u32 color){
|
||||||
int length = strlen(string);
|
int length = strlen(string);
|
||||||
|
|
||||||
for(int i = 0, line_i = 0; i < length; i++, line_i++){
|
for(int i = 0, line_i = 0; i < length; i++, line_i++){
|
||||||
if(string[i] == '\n'){
|
if(string[i] == '\n'){
|
||||||
pos_y += SPACING_VERT;
|
posY += SPACING_Y;
|
||||||
line_i = 0;
|
line_i = 0;
|
||||||
i++;
|
i++;
|
||||||
} else if(line_i >= (SCREEN_TOP_WIDTH - pos_x) / SPACING_HORIZ){
|
} else if(line_i >= (SCREEN_TOP_WIDTH - posX) / SPACING_X){
|
||||||
// Make sure we never get out of the screen.
|
// Make sure we never get out of the screen.
|
||||||
pos_y += SPACING_VERT;
|
posY += SPACING_Y;
|
||||||
line_i = 2; // Little offset so we know the same string continues.
|
line_i = 2; // Little offset so we know the same string continues.
|
||||||
if(string[i] == ' ') i++; // Spaces at the start look weird
|
if(string[i] == ' ') i++; // Spaces at the start look weird
|
||||||
}
|
}
|
||||||
|
|
||||||
drawCharacter(string[i], pos_x + line_i * SPACING_HORIZ, pos_y, color);
|
drawCharacter(string[i], posX + line_i * SPACING_X, posY, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pos_y;
|
return posY;
|
||||||
}
|
}
|
@ -10,10 +10,10 @@
|
|||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
#define SPACING_VERT 10
|
#define SPACING_Y 10
|
||||||
#define SPACING_HORIZ 8
|
#define SPACING_X 8
|
||||||
|
|
||||||
void loadSplash(void);
|
void loadSplash(void);
|
||||||
void clearScreens(void);
|
void clearScreens(void);
|
||||||
void drawCharacter(char character, int pos_x, int pos_y, u32 color);
|
void drawCharacter(char character, int posX, int posY, u32 color);
|
||||||
int drawString(const char *string, int pos_x, int pos_y, u32 color);
|
int drawString(const char *string, int posX, int posY, u32 color);
|
@ -15,17 +15,17 @@ void getEmunandSect(u32 *off, u32 *head, u32 emuNAND){
|
|||||||
u32 nandOffset = emuNAND == 1 ? 0 :
|
u32 nandOffset = emuNAND == 1 ? 0 :
|
||||||
(nandSize > 0x200000 ? 0x400000 : 0x200000);
|
(nandSize > 0x200000 ? 0x400000 : 0x200000);
|
||||||
|
|
||||||
//Check for Gateway emuNAND
|
//Check for RedNAND
|
||||||
if(sdmmc_sdcard_readsectors(nandOffset + nandSize, 1, temp) == 0){
|
if(sdmmc_sdcard_readsectors(nandOffset + 1, 1, temp) == 0){
|
||||||
if(*(u32 *)(temp + 0x100) == NCSD_MAGIC){
|
if(*(u32 *)(temp + 0x100) == NCSD_MAGIC){
|
||||||
|
*off = nandOffset + 1;
|
||||||
|
*head = nandOffset + 1;
|
||||||
|
}
|
||||||
|
//Check for Gateway emuNAND
|
||||||
|
else if(sdmmc_sdcard_readsectors(nandOffset + nandSize, 1, temp) == 0){
|
||||||
|
if(*(u32 *)(temp + 0x100) == NCSD_MAGIC){
|
||||||
*off = nandOffset;
|
*off = nandOffset;
|
||||||
*head = nandOffset + nandSize;
|
*head = nandOffset + nandSize;
|
||||||
}
|
|
||||||
//Check for RedNAND
|
|
||||||
else if(sdmmc_sdcard_readsectors(nandOffset + 1, 1, temp) == 0){
|
|
||||||
if(*(u32 *)(temp + 0x100) == NCSD_MAGIC){
|
|
||||||
*off = nandOffset + 1;
|
|
||||||
*head = nandOffset + 1;
|
|
||||||
}
|
}
|
||||||
//Fallback to the first emuNAND if there's no second one
|
//Fallback to the first emuNAND if there's no second one
|
||||||
else if(emuNAND == 2) getEmunandSect(off, head, 1);
|
else if(emuNAND == 2) getEmunandSect(off, head, 1);
|
||||||
@ -33,32 +33,32 @@ void getEmunandSect(u32 *off, u32 *head, u32 emuNAND){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 getSDMMC(void *pos, u32 size){
|
u32 getSDMMC(u8 *pos, u32 size){
|
||||||
//Look for struct code
|
//Look for struct code
|
||||||
const u8 pattern[] = {0x21, 0x20, 0x18, 0x20};
|
const u8 pattern[] = {0x21, 0x20, 0x18, 0x20};
|
||||||
const u8 *off = (u8 *)memsearch(pos, pattern, size, 4) - 1;
|
const u8 *off = memsearch(pos, pattern, size, 4) - 1;
|
||||||
|
|
||||||
return *(u32 *)(off + 0x0A) + *(u32 *)(off + 0x0E);
|
return *(u32 *)(off + 0x0A) + *(u32 *)(off + 0x0E);
|
||||||
}
|
}
|
||||||
|
|
||||||
void getEmuRW(void *pos, u32 size, u32 *readOff, u32 *writeOff){
|
void getEmuRW(u8 *pos, u32 size, u32 *readOff, u32 *writeOff){
|
||||||
//Look for read/write code
|
//Look for read/write code
|
||||||
const u8 pattern[] = {0x1E, 0x00, 0xC8, 0x05};
|
const u8 pattern[] = {0x1E, 0x00, 0xC8, 0x05};
|
||||||
|
|
||||||
*writeOff = (u32)memsearch(pos, pattern, size, 4) - 6;
|
*readOff = (u32)memsearch(pos, pattern, size, 4) - 6;
|
||||||
*readOff = (u32)memsearch((void *)(*writeOff - 0x1000), pattern, 0x1000, 4) - 6;
|
*writeOff = (u32)memsearch((u8 *)(*readOff + 0xA), pattern, 0x100, 4) - 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 *getMPU(void *pos, u32 size){
|
u32 *getMPU(u8 *pos, u32 size){
|
||||||
//Look for MPU pattern
|
//Look for MPU pattern
|
||||||
const u8 pattern[] = {0x03, 0x00, 0x24, 0x00};
|
const u8 pattern[] = {0x03, 0x00, 0x24, 0x00};
|
||||||
|
|
||||||
return (u32 *)memsearch(pos, pattern, size, 4);
|
return (u32 *)memsearch(pos, pattern, size, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *getEmuCode(u8 *pos, u32 size, u8 *proc9Offset){
|
void *getEmuCode(u8 *proc9Offset){
|
||||||
const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF};
|
const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
|
||||||
|
|
||||||
//Looking for the last free space before Process9
|
//Looking for the last free space before Process9
|
||||||
return (u8 *)memsearch(pos, pattern, size - (size - (u32)(proc9Offset - pos)), 4) + 0xD;
|
return memsearch(proc9Offset - 0x3000, pattern, 0x3000, 6) + 0x455;
|
||||||
}
|
}
|
@ -11,7 +11,7 @@
|
|||||||
#define NCSD_MAGIC (0x4453434E)
|
#define NCSD_MAGIC (0x4453434E)
|
||||||
|
|
||||||
void getEmunandSect(u32 *off, u32 *head, u32 emuNAND);
|
void getEmunandSect(u32 *off, u32 *head, u32 emuNAND);
|
||||||
u32 getSDMMC(void *pos, u32 size);
|
u32 getSDMMC(u8 *pos, u32 size);
|
||||||
void getEmuRW(void *pos, u32 size, u32 *readOff, u32 *writeOff);
|
void getEmuRW(u8 *pos, u32 size, u32 *readOff, u32 *writeOff);
|
||||||
u32 *getMPU(void *pos, u32 size);
|
u32 *getMPU(u8 *pos, u32 size);
|
||||||
void *getEmuCode(u8 *pos, u32 size, u8 *proc9Offset);
|
void *getEmuCode(u8 *proc9Offset);
|
@ -18,7 +18,7 @@
|
|||||||
#include "../build/patches.h"
|
#include "../build/patches.h"
|
||||||
|
|
||||||
//FIRM patches version
|
//FIRM patches version
|
||||||
#define PATCH_VER 1
|
#define PATCH_VER 2
|
||||||
|
|
||||||
static firmHeader *const firm = (firmHeader *)0x24000000;
|
static firmHeader *const firm = (firmHeader *)0x24000000;
|
||||||
static const firmSectionHeader *section;
|
static const firmSectionHeader *section;
|
||||||
@ -48,8 +48,8 @@ void setupCFW(void){
|
|||||||
|
|
||||||
//Attempt to read the configuration file
|
//Attempt to read the configuration file
|
||||||
const char configPath[] = "aurei/config.bin";
|
const char configPath[] = "aurei/config.bin";
|
||||||
u16 config = 0;
|
u32 config = 0;
|
||||||
u32 needConfig = fileRead(&config, configPath, 2) ? 1 : 2;
|
u32 needConfig = fileRead(&config, configPath, 3) ? 1 : 2;
|
||||||
|
|
||||||
//Determine if A9LH is installed and the user has an updated sysNAND
|
//Determine if A9LH is installed and the user has an updated sysNAND
|
||||||
u32 updatedSys;
|
u32 updatedSys;
|
||||||
@ -66,21 +66,18 @@ void setupCFW(void){
|
|||||||
updatedSys = 0;
|
updatedSys = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Determine if the user chose to use pre-patched FIRMs
|
|
||||||
u32 usePatchedFirmSet = (config >> 1) & 0x1;
|
|
||||||
|
|
||||||
/* If booting with A9LH, it's a MCU reboot and a previous configuration exists,
|
/* If booting with A9LH, it's a MCU reboot and a previous configuration exists,
|
||||||
try to force boot options */
|
try to force boot options */
|
||||||
if(a9lhBoot && previousFirm && needConfig == 1){
|
if(a9lhBoot && previousFirm && needConfig == 1){
|
||||||
//Always force a sysNAND boot when quitting AGB_FIRM
|
//Always force a sysNAND boot when quitting AGB_FIRM
|
||||||
if(previousFirm == 0x7){
|
if(previousFirm == 0x7){
|
||||||
if(!updatedSys) mode = (config >> 8) & 0x1;
|
if(!updatedSys) mode = (config >> 12) & 0x1;
|
||||||
emuNAND = 0;
|
emuNAND = 0;
|
||||||
needConfig = 0;
|
needConfig = 0;
|
||||||
//Else, force the last used boot options unless A, L or R are pressed
|
//Else, force the last used boot options unless A, L or R are pressed
|
||||||
} else if(!(pressed & OPTION_BUTTONS)){
|
} else if(!(pressed & OPTION_BUTTONS)){
|
||||||
mode = (config >> 8) & 0x1;
|
mode = (config >> 12) & 0x1;
|
||||||
emuNAND = (config >> 9) & 0x1;
|
emuNAND = (config >> 13) & 0x3;
|
||||||
needConfig = 0;
|
needConfig = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,45 +91,45 @@ void setupCFW(void){
|
|||||||
|
|
||||||
//If no configuration file exists or SELECT is held, load configuration menu
|
//If no configuration file exists or SELECT is held, load configuration menu
|
||||||
if(needConfig == 2 || (pressed & BUTTON_SELECT))
|
if(needConfig == 2 || (pressed & BUTTON_SELECT))
|
||||||
configureCFW(configPath);
|
configureCFW(configPath, patchedFirms[3]);
|
||||||
|
|
||||||
//If screens are inited, load splash screen
|
//If screens are inited, load splash screen
|
||||||
if(PDN_GPU_CNT != 0x1) loadSplash();
|
if(PDN_GPU_CNT != 0x1) loadSplash();
|
||||||
|
|
||||||
/* If L is pressed, boot 9.0 FIRM */
|
/* If L is pressed, boot 9.0 FIRM */
|
||||||
mode = (pressed & BUTTON_L1) ? 0 : 1;
|
mode = ((config >> 3) & 0x1) ? ((!(pressed & BUTTON_L1R1)) ? 0 : 1) :
|
||||||
|
((pressed & BUTTON_L1) ? 0 : 1);
|
||||||
|
|
||||||
/* If L or R aren't pressed on a 9.0/9.2 sysNAND, or the 9.0 FIRM is selected
|
/* If L or R aren't pressed on a 9.0/9.2 sysNAND, or the 9.0 FIRM is selected
|
||||||
or R is pressed on a > 9.2 sysNAND, boot emuNAND */
|
or R is pressed on a > 9.2 sysNAND, boot emuNAND */
|
||||||
if((updatedSys && (!mode || (pressed & BUTTON_R1))) ||
|
if((updatedSys && (!mode || (pressed & BUTTON_R1))) ||
|
||||||
(!updatedSys && mode && !(pressed & BUTTON_R1))){
|
(!updatedSys && mode && !(pressed & BUTTON_R1))){
|
||||||
//If not 9.0 FIRM and B is pressed, attempt booting the second emuNAND
|
//If not 9.0 FIRM and B is pressed, attempt booting the second emuNAND
|
||||||
emuNAND = (mode && (pressed & BUTTON_B)) ? 2 : 1;
|
emuNAND = (mode && ((!(pressed & BUTTON_B)) == ((config >> 4) & 0x1))) ? 2 : 1;
|
||||||
} else emuNAND = 0;
|
} else emuNAND = 0;
|
||||||
|
|
||||||
|
u32 tempConfig = (PATCH_VER << 17) | (a9lhSetup << 16) | (emuNAND << 13) | (mode << 12);
|
||||||
|
|
||||||
/* If tha FIRM patches version is different or user switched to/from A9LH,
|
/* If tha FIRM patches version is different or user switched to/from A9LH,
|
||||||
and "Use pre-patched FIRMs" is set, delete all patched FIRMs */
|
delete all patched FIRMs */
|
||||||
u16 bootConfig = (PATCH_VER << 11) | (a9lhSetup << 10);
|
if((tempConfig & 0xFF0000) != (config & 0xFF0000))
|
||||||
if(usePatchedFirmSet && bootConfig != (config & 0xFC00))
|
|
||||||
deleteFirms(patchedFirms, sizeof(patchedFirms) / sizeof(char *));
|
deleteFirms(patchedFirms, sizeof(patchedFirms) / sizeof(char *));
|
||||||
|
|
||||||
//We also need to remember the used boot mode on A9LH
|
|
||||||
if(a9lhBoot) bootConfig |= (mode << 8) | (emuNAND << 9);
|
|
||||||
|
|
||||||
//If the boot configuration is different from previously, overwrite it
|
//If the boot configuration is different from previously, overwrite it
|
||||||
if(bootConfig != (config & 0xFF00)){
|
if((tempConfig & 0xFFF000) != (config & 0xFFF000)){
|
||||||
//Preserve user settings (first byte)
|
//Preserve user settings (first 12 bits)
|
||||||
u16 tempConfig = ((config & 0xFF) | bootConfig);
|
tempConfig |= config & 0xFFF;
|
||||||
fileWrite(&tempConfig, configPath, 2);
|
fileWrite(&tempConfig, configPath, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Determine which patched FIRM we need to write or attempt to use (if any)
|
/* Determine which patched FIRM we need to write or attempt to use (if any).
|
||||||
|
Patched 9.0 FIRM is only needed if "Use pre-patched FIRMs" is set */
|
||||||
selectedFirm = mode ? (emuNAND ? (emuNAND == 1 ? 2 : 3) : 1) :
|
selectedFirm = mode ? (emuNAND ? (emuNAND == 1 ? 2 : 3) : 1) :
|
||||||
(usePatchedFirmSet ? 4 : 0);
|
(((config >> 1) & 0x1) ? 4 : 0);
|
||||||
|
|
||||||
//Determine if we need to use a pre-patched FIRM
|
//If "Use pre-patched FIRMs" is set and the appropriate FIRM exists, use it
|
||||||
usePatchedFirm = (usePatchedFirmSet && fileExists(patchedFirms[selectedFirm - 1])) ? 1 : 0;
|
usePatchedFirm = (((config >> 1) & 0x1) && fileExists(patchedFirms[selectedFirm - 1])) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Load FIRM into FCRAM
|
//Load FIRM into FCRAM
|
||||||
@ -182,7 +179,7 @@ static void loadEmu(u8 *proc9Offset){
|
|||||||
if(!emuHeader) error("No emuNAND has been detected");
|
if(!emuHeader) error("No emuNAND has been detected");
|
||||||
|
|
||||||
//Copy emuNAND code
|
//Copy emuNAND code
|
||||||
void *emuCodeOffset = getEmuCode(arm9Section, section[2].size, proc9Offset);
|
void *emuCodeOffset = getEmuCode(proc9Offset);
|
||||||
memcpy(emuCodeOffset, emunand, emunand_size);
|
memcpy(emuCodeOffset, emunand, emunand_size);
|
||||||
|
|
||||||
//Add the data of the found emuNAND
|
//Add the data of the found emuNAND
|
||||||
@ -266,6 +263,18 @@ void patchFirm(void){
|
|||||||
*(u16 *)sigOffset2 = sigPatch[0];
|
*(u16 *)sigOffset2 = sigPatch[0];
|
||||||
*((u16 *)sigOffset2 + 1) = sigPatch[1];
|
*((u16 *)sigOffset2 + 1) = sigPatch[1];
|
||||||
|
|
||||||
|
//Replace the FIRM loader with the injector
|
||||||
|
u32 loaderOffset,
|
||||||
|
loaderSize;
|
||||||
|
|
||||||
|
getLoader((u8 *)firm + section[0].offset, section[0].size, &loaderOffset, &loaderSize);
|
||||||
|
if(injector_size <= (int)loaderSize){
|
||||||
|
memset((void *)loaderOffset, 0, loaderSize);
|
||||||
|
memcpy((void *)loaderOffset, injector, injector_size);
|
||||||
|
*((u32 *)loaderOffset + 0x41) = loaderSize / 0x200;
|
||||||
|
*((u32 *)loaderOffset + 0x69) = loaderSize / 0x200 - 5;
|
||||||
|
}
|
||||||
|
|
||||||
//Patch ARM9 entrypoint on N3DS to skip arm9loader
|
//Patch ARM9 entrypoint on N3DS to skip arm9loader
|
||||||
if(console)
|
if(console)
|
||||||
firm->arm9Entry = (u8 *)0x801B01C;
|
firm->arm9Entry = (u8 *)0x801B01C;
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
* memory.c
|
* memory.c
|
||||||
* by Reisyukaku / Aurora Wright
|
* by Reisyukaku / Aurora Wright
|
||||||
* Copyright (c) 2016 All Rights Reserved
|
* Copyright (c) 2016 All Rights Reserved
|
||||||
|
*
|
||||||
|
* Quick Search algorithm adapted from http://igm.univ-mlv.fr/~lecroq/string/node19.html#SECTION00190
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
@ -35,8 +37,25 @@ int memcmp(const void *buf1, const void *buf2, u32 size){
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *memsearch(void *start_pos, const void *search, u32 size, u32 size_search){
|
u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize){
|
||||||
for(u8 *pos = (u8 *)start_pos + size - size_search; pos >= (u8 *)start_pos; pos--)
|
const u8 *patternc = (const u8 *)pattern;
|
||||||
if(memcmp(pos, search, size_search) == 0) return pos;
|
|
||||||
|
//Preprocessing
|
||||||
|
int table[256];
|
||||||
|
|
||||||
|
for(u32 i = 0; i < 256; ++i)
|
||||||
|
table[i] = patternSize + 1;
|
||||||
|
for(u32 i = 0; i < patternSize; ++i)
|
||||||
|
table[patternc[i]] = patternSize - i;
|
||||||
|
|
||||||
|
//Searching
|
||||||
|
u32 j = 0;
|
||||||
|
|
||||||
|
while(j <= size - patternSize){
|
||||||
|
if(memcmp(patternc, startPos + j, patternSize) == 0)
|
||||||
|
return startPos + j;
|
||||||
|
j += table[startPos[j + patternSize]];
|
||||||
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
@ -2,6 +2,8 @@
|
|||||||
* memory.h
|
* memory.h
|
||||||
* by Reisyukaku / Aurora Wright
|
* by Reisyukaku / Aurora Wright
|
||||||
* Copyright (c) 2016 All Rights Reserved
|
* Copyright (c) 2016 All Rights Reserved
|
||||||
|
*
|
||||||
|
* Quick Search algorithm adapted from http://igm.univ-mlv.fr/~lecroq/string/node19.html#SECTION00190
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@ -12,4 +14,4 @@ void memcpy(void *dest, const void *src, u32 size);
|
|||||||
void memset(void *dest, int filler, u32 size);
|
void memset(void *dest, int filler, u32 size);
|
||||||
void memset32(void *dest, u32 filler, u32 size);
|
void memset32(void *dest, u32 filler, u32 size);
|
||||||
int memcmp(const void *buf1, const void *buf2, u32 size);
|
int memcmp(const void *buf1, const void *buf2, u32 size);
|
||||||
void *memsearch(void *start_pos, const void *search, u32 size, u32 size_search);
|
u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize);
|
@ -23,24 +23,24 @@ const u16 writeBlock[2] = {0x2000, 0x46C0};
|
|||||||
* Functions
|
* Functions
|
||||||
**************************************************/
|
**************************************************/
|
||||||
|
|
||||||
u8 *getProc9(void *pos, u32 size){
|
u8 *getProc9(u8 *pos, u32 size){
|
||||||
return (u8 *)memsearch(pos, "ess9", size, 4);
|
return memsearch(pos, "ess9", size, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
void getSigChecks(void *pos, u32 size, u32 *off, u32 *off2){
|
void getSigChecks(u8 *pos, u32 size, u32 *off, u32 *off2){
|
||||||
//Look for signature checks
|
//Look for signature checks
|
||||||
const u8 pattern[] = {0xC0, 0x1C, 0x76, 0xE7};
|
const u8 pattern[] = {0xC0, 0x1C, 0x76, 0xE7},
|
||||||
const u8 pattern2[] = {0xB5, 0x22, 0x4D, 0x0C};
|
pattern2[] = {0xB5, 0x22, 0x4D, 0x0C};
|
||||||
|
|
||||||
*off = (u32)memsearch(pos, pattern, size, 4);
|
*off = (u32)memsearch(pos, pattern, size, 4);
|
||||||
*off2 = (u32)memsearch(pos, pattern2, size, 4) - 1;
|
*off2 = (u32)memsearch(pos, pattern2, size, 4) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *getReboot(void *pos, u32 size){
|
void *getReboot(u8 *pos, u32 size){
|
||||||
//Look for FIRM reboot code
|
//Look for FIRM reboot code
|
||||||
const u8 pattern[] = {0xDE, 0x1F, 0x8D, 0xE2};
|
const u8 pattern[] = {0xDE, 0x1F, 0x8D, 0xE2};
|
||||||
|
|
||||||
return (u8 *)memsearch(pos, pattern, size, 4) - 0x10;
|
return memsearch(pos, pattern, size, 4) - 0x10;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 getfOpen(u8 *proc9Offset, void *rebootOffset){
|
u32 getfOpen(u8 *proc9Offset, void *rebootOffset){
|
||||||
@ -53,10 +53,17 @@ u32 getfOpen(u8 *proc9Offset, void *rebootOffset){
|
|||||||
return (u32)rebootOffset + 9 - (-((*(u32 *)rebootOffset & 0x00FFFFFF) << 2) & 0xFFFFF) - p9CodeOff + p9MemAddr;
|
return (u32)rebootOffset + 9 - (-((*(u32 *)rebootOffset & 0x00FFFFFF) << 2) & 0xFFFFF) - p9CodeOff + p9MemAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 *getFirmWrite(void *pos, u32 size){
|
u16 *getFirmWrite(u8 *pos, u32 size){
|
||||||
//Look for FIRM writing code
|
//Look for FIRM writing code
|
||||||
u8 *const off = (u8 *)memsearch(pos, "exe:", size, 4);
|
u8 *const off = memsearch(pos, "exe:", size, 4);
|
||||||
const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
|
const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
|
||||||
|
|
||||||
return (u16 *)memsearch(off - 0x100, pattern, 0x100, 4);
|
return (u16 *)memsearch(off - 0x100, pattern, 0x100, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getLoader(u8 *pos, u32 size, u32 *loaderOffset, u32 *loaderSize){
|
||||||
|
u8 *const off = memsearch(pos, "loade", size, 5);
|
||||||
|
|
||||||
|
*loaderOffset = (u32)off - 0x200;
|
||||||
|
*loaderSize = *(u32 *)(off - 0xFC) * 0x200;
|
||||||
}
|
}
|
@ -19,8 +19,9 @@ const u16 writeBlock[2];
|
|||||||
/**************************************************
|
/**************************************************
|
||||||
* Functions
|
* Functions
|
||||||
**************************************************/
|
**************************************************/
|
||||||
u8 *getProc9(void *pos, u32 size);
|
u8 *getProc9(u8 *pos, u32 size);
|
||||||
void getSigChecks(void *pos, u32 size, u32 *off, u32 *off2);
|
void getSigChecks(u8 *pos, u32 size, u32 *off, u32 *off2);
|
||||||
void *getReboot(void *pos, u32 size);
|
void *getReboot(u8 *pos, u32 size);
|
||||||
u32 getfOpen(u8 *proc9Offset, void *rebootOffset);
|
u32 getfOpen(u8 *proc9Offset, void *rebootOffset);
|
||||||
u16 *getFirmWrite(void *pos, u32 size);
|
u16 *getFirmWrite(u8 *pos, u32 size);
|
||||||
|
void getLoader(u8 *pos, u32 size, u32 *loaderOffset, u32 *loaderSize);
|
@ -11,19 +11,14 @@
|
|||||||
#include "i2c.h"
|
#include "i2c.h"
|
||||||
#include "buttons.h"
|
#include "buttons.h"
|
||||||
|
|
||||||
//Number of options that can be configured
|
|
||||||
#define OPTIONS 3
|
|
||||||
|
|
||||||
#define COLOR_TITLE 0xFF9900
|
#define COLOR_TITLE 0xFF9900
|
||||||
#define COLOR_WHITE 0xFFFFFF
|
#define COLOR_WHITE 0xFFFFFF
|
||||||
#define COLOR_RED 0x0000FF
|
#define COLOR_RED 0x0000FF
|
||||||
#define COLOR_BLACK 0x000000
|
#define COLOR_BLACK 0x000000
|
||||||
|
|
||||||
struct options {
|
struct option {
|
||||||
char *text[OPTIONS];
|
int posY;
|
||||||
int pos_y[OPTIONS];
|
u32 enabled;
|
||||||
u32 enabled[OPTIONS];
|
|
||||||
u32 selected;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static u16 waitInput(void){
|
static u16 waitInput(void){
|
||||||
@ -48,52 +43,75 @@ static u16 waitInput(void){
|
|||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
void configureCFW(const char *configPath){
|
void configureCFW(const char *configPath, const char *firm90Path){
|
||||||
struct options options;
|
|
||||||
|
|
||||||
options.text[0] = "( ) Updated SysNAND mode (A9LH-only)";
|
|
||||||
options.text[1] = "( ) Use pre-patched FIRMs";
|
|
||||||
options.text[2] = "( ) Force A9LH detection";
|
|
||||||
|
|
||||||
initScreens();
|
initScreens();
|
||||||
|
|
||||||
drawString(CONFIG_TITLE, 10, 10, COLOR_TITLE);
|
drawString(CONFIG_TITLE, 10, 10, COLOR_TITLE);
|
||||||
drawString("Press A to select, START to save and reboot", 10, 30, COLOR_WHITE);
|
drawString("Press A to select, START to save and reboot", 10, 30, COLOR_WHITE);
|
||||||
|
|
||||||
|
const char *optionsText[] = { "( ) Updated SysNAND mode (A9LH-only)",
|
||||||
|
"( ) Use pre-patched FIRMs",
|
||||||
|
"( ) Force A9LH detection",
|
||||||
|
"( ) Use 9.0 FIRM as default",
|
||||||
|
"( ) Use second EmuNAND as default",
|
||||||
|
"( ) Show current NAND in System Settings" };
|
||||||
|
|
||||||
|
u32 optionsAmount = sizeof(optionsText) / sizeof(char *);
|
||||||
|
struct option options[optionsAmount];
|
||||||
|
|
||||||
//Read and parse the existing configuration
|
//Read and parse the existing configuration
|
||||||
u16 tempConfig = 0;
|
u32 tempConfig = 0;
|
||||||
fileRead(&tempConfig, configPath, 2);
|
fileRead(&tempConfig, configPath, 3);
|
||||||
for(u32 i = 0; i < OPTIONS; i++)
|
for(u32 i = 0; i < optionsAmount; i++)
|
||||||
options.enabled[i] = (tempConfig >> i) & 0x1;
|
options[i].enabled = (tempConfig >> i) & 0x1;
|
||||||
|
|
||||||
//Pre-select the first configuration option
|
//Pre-select the first configuration option
|
||||||
options.selected = 0;
|
u32 selectedOption = 0;
|
||||||
|
|
||||||
//Boring configuration menu
|
//Boring configuration menu
|
||||||
while(1){
|
while(1){
|
||||||
u16 pressed = 0;
|
u16 pressed = 0;
|
||||||
|
|
||||||
do{
|
do{
|
||||||
for(u32 i = 0; i < OPTIONS; i++){
|
for(u32 i = 0; i < optionsAmount; i++){
|
||||||
options.pos_y[i] = drawString(options.text[i], 10, !i ? 60 : options.pos_y[i - 1] + SPACING_VERT, options.selected == i ? COLOR_RED : COLOR_WHITE);
|
options[i].posY = drawString(optionsText[i], 10, !i ? 60 : options[i - 1].posY + SPACING_Y, selectedOption == i ? COLOR_RED : COLOR_WHITE);
|
||||||
drawCharacter('x', 10 + SPACING_HORIZ, options.pos_y[i], options.enabled[i] ? (options.selected == i ? COLOR_RED : COLOR_WHITE) : COLOR_BLACK);
|
drawCharacter('x', 10 + SPACING_X, options[i].posY, options[i].enabled ? (selectedOption == i ? COLOR_RED : COLOR_WHITE) : COLOR_BLACK);
|
||||||
}
|
}
|
||||||
pressed = waitInput();
|
pressed = waitInput();
|
||||||
} while(!(pressed & (BUTTON_UP | BUTTON_DOWN | BUTTON_A | BUTTON_START)));
|
} while(!(pressed & MENU_BUTTONS));
|
||||||
|
|
||||||
if(pressed == BUTTON_UP) options.selected = !options.selected ? OPTIONS - 1 : options.selected - 1;
|
switch(pressed){
|
||||||
else if(pressed == BUTTON_DOWN) options.selected = options.selected == OPTIONS - 1 ? 0 : options.selected + 1;
|
case BUTTON_UP:
|
||||||
else if(pressed == BUTTON_A) options.enabled[options.selected] = !options.enabled[options.selected];
|
selectedOption = !selectedOption ? optionsAmount - 1 : selectedOption - 1;
|
||||||
else if(pressed == BUTTON_START) break;
|
break;
|
||||||
|
case BUTTON_DOWN:
|
||||||
|
selectedOption = selectedOption == optionsAmount - 1 ? 0 : selectedOption + 1;
|
||||||
|
break;
|
||||||
|
case BUTTON_LEFT:
|
||||||
|
selectedOption = 0;
|
||||||
|
break;
|
||||||
|
case BUTTON_RIGHT:
|
||||||
|
selectedOption = optionsAmount - 1;
|
||||||
|
break;
|
||||||
|
case BUTTON_A:
|
||||||
|
options[selectedOption].enabled = !options[selectedOption].enabled;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pressed == BUTTON_START) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Preserve the last-used boot options (second byte)
|
//If the user has been using A9LH and the "Updated SysNAND" setting changed, delete the patched 9.0 FIRM
|
||||||
tempConfig &= 0xFF00;
|
if(((tempConfig >> 16) & 0x1) && ((tempConfig & 0x1) != options[0].enabled))
|
||||||
|
fileDelete(firm90Path);
|
||||||
|
|
||||||
|
//Preserve the last-used boot options (last 12 bits)
|
||||||
|
tempConfig &= 0xFFF000;
|
||||||
|
|
||||||
//Parse and write the selected options
|
//Parse and write the selected options
|
||||||
for(u32 i = 0; i < OPTIONS; i++)
|
for(u32 i = 0; i < optionsAmount; i++)
|
||||||
tempConfig |= options.enabled[i] << i;
|
tempConfig |= options[i].enabled << i;
|
||||||
fileWrite(&tempConfig, configPath, 2);
|
fileWrite(&tempConfig, configPath, 3);
|
||||||
|
|
||||||
//Reboot
|
//Reboot
|
||||||
i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 2);
|
i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 2);
|
||||||
@ -111,8 +129,8 @@ void error(const char *message){
|
|||||||
initScreens();
|
initScreens();
|
||||||
|
|
||||||
drawString("An error has occurred:", 10, 10, COLOR_RED);
|
drawString("An error has occurred:", 10, 10, COLOR_RED);
|
||||||
int pos_y = drawString(message, 10, 30, COLOR_WHITE);
|
int posY = drawString(message, 10, 30, COLOR_WHITE);
|
||||||
drawString("Press any button to shutdown", 10, pos_y + 2 * SPACING_VERT, COLOR_WHITE);
|
drawString("Press any button to shutdown", 10, posY + 2 * SPACING_Y, COLOR_WHITE);
|
||||||
|
|
||||||
waitInput();
|
waitInput();
|
||||||
|
|
||||||
|
@ -8,6 +8,6 @@
|
|||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
void configureCFW(const char *configPath);
|
void configureCFW(const char *configPath, const char *firm90Path);
|
||||||
void deleteFirms(const char *firmPaths[], u32 firms);
|
void deleteFirms(const char *firmPaths[], u32 firms);
|
||||||
void error(const char *message);
|
void error(const char *message);
|
Reference in New Issue
Block a user