]> Chaos Git - corbenik/corbenik.git/commitdiff
Loader? loader.
authorroot <chaos.kagami@gmail.com>
Tue, 17 May 2016 05:17:33 +0000 (01:17 -0400)
committerroot <chaos.kagami@gmail.com>
Tue, 17 May 2016 05:17:33 +0000 (01:17 -0400)
32 files changed:
Makefile
README.md
doc/compiling.md [new file with mode: 0644]
doc/features.md
doc/intended-features.md [new file with mode: 0644]
doc/nonos.md [new file with mode: 0644]
external/Makefile [new file with mode: 0644]
external/loader/LICENSE [new file with mode: 0644]
external/loader/Makefile [new file with mode: 0644]
external/loader/README.md [new file with mode: 0644]
external/loader/loader.rsf [new file with mode: 0644]
external/loader/source/config.h [new file with mode: 0644]
external/loader/source/exheader.h [new file with mode: 0755]
external/loader/source/fsldr.c [new file with mode: 0644]
external/loader/source/fsldr.h [new file with mode: 0644]
external/loader/source/fsreg.c [new file with mode: 0644]
external/loader/source/fsreg.h [new file with mode: 0644]
external/loader/source/ifile.c [new file with mode: 0644]
external/loader/source/ifile.h [new file with mode: 0644]
external/loader/source/loader.c [new file with mode: 0644]
external/loader/source/memory.c [new file with mode: 0644]
external/loader/source/memory.h [new file with mode: 0644]
external/loader/source/patcher.c [new file with mode: 0644]
external/loader/source/patcher.h [new file with mode: 0644]
external/loader/source/pxipm.c [new file with mode: 0644]
external/loader/source/pxipm.h [new file with mode: 0644]
external/loader/source/srvsys.c [new file with mode: 0644]
external/loader/source/srvsys.h [new file with mode: 0644]
source/config.c
source/main.c
source/std/draw.c
vco/template/Makefile

index c71c9a3bebf4c531a326d77f5d5ae8fb24da79e0..3d51d573310e60e8c6b7d1fd18c4be7ed4179f70 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -28,18 +28,23 @@ objects_cfw = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
                          $(call rwildcard, $(dir_source), *.s *.c)))
 
 .PHONY: all
-all: a9lh vco
+all: a9lh vco external
 
 .PHONY: vco
 vco:
        make -C vco
 
+.PHONY: external
+external:
+       make -C external
+
 .PHONY: a9lh
 a9lh: $(dir_out)/arm9loaderhax.bin
 
 .PHONY: clean
 clean:
        make -C vco clean
+       make -C external clean
        rm -rf $(dir_out) $(dir_build)
 
 .PHONY: $(dir_out)/arm9loaderhax.bin
@@ -63,35 +68,35 @@ $(dir_build)/%.o: $(dir_source)/%.s
 
 $(dir_build)/fatfs/%.o: $(dir_source)/fatfs/%.c
        @mkdir -p "$(@D)"
-       $(COMPILE.c) -mthumb -mthumb-interwork -Wno-unused-function $(OUTPUT_OPTION) $<
+       $(COMPILE.c) -Wno-unused-function $(OUTPUT_OPTION) $<
 
 $(dir_build)/fatfs/%.o: $(dir_source)/fatfs/%.s
        @mkdir -p "$(@D)"
-       $(COMPILE.s) -mthumb -mthumb-interwork $(OUTPUT_OPTION) $<
+       $(COMPILE.s) $(OUTPUT_OPTION) $<
 
 $(dir_build)/std/%.o: $(dir_source)/std/%.c
        @mkdir -p "$(@D)"
-       $(COMPILE.c) -mthumb -mthumb-interwork -Wno-unused-function $(OUTPUT_OPTION) $<
+       $(COMPILE.c) -Wno-unused-function $(OUTPUT_OPTION) $<
 
 $(dir_build)/std/%.o: $(dir_source)/std/%.s
        @mkdir -p "$(@D)"
-       $(COMPILE.s) -mthumb -mthumb-interwork $(OUTPUT_OPTION) $<
+       $(COMPILE.s) $(OUTPUT_OPTION) $<
 
 $(dir_build)/firm/%.o: $(dir_source)/firm/%.c
        @mkdir -p "$(@D)"
-       $(COMPILE.c) -mthumb -mthumb-interwork -Wno-unused-function $(OUTPUT_OPTION) $<
+       $(COMPILE.c) -Wno-unused-function $(OUTPUT_OPTION) $<
 
 $(dir_build)/firm/%.o: $(dir_source)/firm/%.s
        @mkdir -p "$(@D)"
-       $(COMPILE.s) -mthumb -mthumb-interwork $(OUTPUT_OPTION) $<
+       $(COMPILE.s) $(OUTPUT_OPTION) $<
 
 
 $(dir_build)/patch/%.o: $(dir_source)/patch/%.c
        @mkdir -p "$(@D)"
-       $(COMPILE.c) -mthumb -mthumb-interwork -Wno-unused-function $(OUTPUT_OPTION) $<
+       $(COMPILE.c) -Wno-unused-function $(OUTPUT_OPTION) $<
 
 $(dir_build)/patch/%.o: $(dir_source)/patch/%.s
        @mkdir -p "$(@D)"
-       $(COMPILE.s) -mthumb -mthumb-interwork $(OUTPUT_OPTION) $<
+       $(COMPILE.s) $(OUTPUT_OPTION) $<
 
 include $(call rwildcard, $(dir_build), *.d)
index 838ce4f31a182f1d99683e515d42e752f646be50..b0d0d329a0f558bf9c802f75e2156a2c1eb9c96a 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,18 +1,16 @@
 Corbenik
 ==============================
 
-This is (yet another) CFW for the 3DS. Unlike other CFWs, this was mostly written from scratch for fun, and because I'm a control freak. Some parts are inherited from other CFW - near everything in src/firm is based on CakesFW, and the signature patch/firmprot/svcbackdoor fix are all based on Luma3DS while using the more correct CakesFW section code.
+This is (yet another) CFW for the 3DS. Unlike other CFWs, this was mostly written from scratch for fun, and because I'm a control freak. Some parts are inherited from other CFW - near everything in src/firm is based on CakesFW, and the signature patch/firmprot/svcbackdoor fix are all based on Luma3DS while using the more correct CakesFW section code versus pointer math magic.
 
-Conceptually, and in operation, this is most similar to mid-kid/CakesForeveryWan out of the bunch. That is, it uses external patches from the filesystem and is intended for developers and control freaks. Unlike cakes, patches are dynamically linked code for whatever processor it is on.
+Eventually in operation, this will be most similar to mid-kid/CakesForeveryWan out of the bunch. That is, it will use external patches from the filesystem and is intended for developers and control freaks. Unlike cakes, patches will be dynamically loaded code for arm9/arm11 which will be relocated against internal functions, allowing relatively tiny patches. This is not stable yet, and does not work yet.
 
-Yes; you read that correctly. I initially was going to use a dynamic cake-like patch. I quickly realized a fatal flaw in any "patch" format: what you can do from a patch is limited to what the parser handles. The best way to fix this was to make patches standalone relocatable binaries instead.
+I was initially going to use a dynamic cake-like patch, but I quickly realized a fatal flaw in any "patch" format: what you can do from a patch is limited to what the parser handles. The best way to fix is to simply externalize patches; as yifan lu suggested even that loader should use a bytecode VM.
 
-The binary ABI is not yet stabilized. Do not expect a patch to simply function a version later. For this very reason, the CFW version field in the patch header is ignored at the moment until the ABI has been finalized. I *may* simply rewrite it as an elf loader; I'm unsure yet as to what I'll do.
-
-If you want to know how Corbenik sizes up to other CFWs - see `doc/features.md`.
+If you want to know how Corbenik sizes up to other CFWs as of NOW - see `doc/features.md`. If you want to see how it will hopefully size up once I'm finished with all the code see `doc/intended-comparison.md`
 
 For compilation instructions, see `doc/compiling.md`.
 
-Unless otherwise noted, everything in this repo can be used under the terms of the GNU GPL, Version 3 or later (if ever) at your discretion. This includes situations where there's no copyright header within a source file.
+Unless otherwise noted, everything in this repo can be used under the terms of the GNU GPL, Version 3 or later (if ever) at your discretion. This includes situations where there's no copyright header within a source file. I get lazy with those; assume everything can be used under the GPL.
 
 Technically, all patches must be open source under a compatible license as well due to these linking restrictions. I will not be making the linking exception. Allowing proprietary patches to exist will only harm everyone in the homebrew community in long-term. Read: NTR.
diff --git a/doc/compiling.md b/doc/compiling.md
new file mode 100644 (file)
index 0000000..58dc7fb
--- /dev/null
@@ -0,0 +1,17 @@
+Cloning / Compilation
+--------------------------
+
+Okay, first thing's first. I can't support compiling on Windows. Use Linux. The repo abuses symbolic links links with good reason.
+
+You'll need the following to compile:
+
+ * git
+ * gcc (as in, host gcc)
+ * arm-none-eabi-gcc (as in, devkitarm OR baremetal...but I can't necessarily help with the latter)
+ * a brain (hard requirement)
+
+To clone: use `git clone --recursive`. Some parts of this CFW are submodules.
+
+To build: run `make`. Output is in `out`. Done.
+
+Not every revision is 100% guaranteed to build. I'll set up a CI eventually.
index 6d98ee8f7e8bab1ec362756ff57a39e2135b65f1..1ef477997f138eb2d8f165de2a536b0bc7c8614b 100644 (file)
@@ -10,7 +10,7 @@ Feature graph
 +--------+-----------------+-----------------+------------------------------+-----------+---------------+-------------------+-------+
 |ReiNAND |Meme(SD)         |Builtin, Dynamic |Sig,Emu,Sys,Mod,Ptc           |N/A        |Readability    |Minimalist         | [4]   |
 +--------+-----------------+-----------------+------------------------------+-----------+---------------+-------------------+-------+
-|Corbenik|Dec/Enc (SD)     |Executable       |Sig,Ptc,Bck,Mod,Sys           |Mis        |Read/Speed     |Advanced Devs      | [5]   |
+|Corbenik|Dec/Enc (SD)     |Builtin, Dynamic |Sig,Ptc,Sys                   |N/A        |Read/Speed     |Advanced Devs      | [5]   |
 +--------+-----------------+-----------------+------------------------------+-----------+---------------+-------------------+-------+
 |NTR     |N/A              |Executable       |Mis                           |Mis        |Douchebaggery  |Shilling Closed Src| [6]   |
 +--------+-----------------+-----------------+------------------------------+-----------+---------------+-------------------+-------+
@@ -60,7 +60,7 @@ Misc features by CFW:
     My focus will always be readability over speed, unless choosing speed and adding additional documentation
     suffices. As an aside; if you see a single trigraph in my code, please report it. Trigraphs are by design a bug.
 
-[2] CakesFW uses a patch format that has static offsets. They're dynamically loaded and in theory are impervious
+[2] CakesFW uses a patch format that has static offsets. They're loaded off FS and in theory are impervious
     to updates. In practice, updating patches is a pain and better done by offset patches like Luma3DS/Rei.
 
     Either way, Cakes allows doing some things the other firmware authors would probably call idiotic; I call them
@@ -77,11 +77,7 @@ Misc features by CFW:
     ass magic to load a re-encrypted copy (?) of the nintendo firmware using a key refered to as the 'memekey'. How
     encrypting data differently makes it any less illegal to rehost I have no clue.
 
-[5] Yes, Corbenik currently lacks quite a few things from this graph. On the upside; patches can be implemented
-    externally. I currently have zero interest in implementing reboot; I never need it, having a n3ds. EmuNAND, as
-    well is something I have little interest in (but will happen eventually.)
-
-    As for other bits; Aside from fixing svcBackdoor, I can also inject arbitrary custom services. You're welcome.
+[5] Yes, Corbenik currently lacks quite a few things from this graph. Quite. A. Few. This will change with time.
 
 [6] Okay, first - I could rant about how NTR is harming us long run for hours. I won't rant for hours, but I will
     explain some of the rationale here:
diff --git a/doc/intended-features.md b/doc/intended-features.md
new file mode 100644 (file)
index 0000000..6d98ee8
--- /dev/null
@@ -0,0 +1,101 @@
+Feature graph
+----------------
+
++--------+-----------------+-----------------+------------------------------+-----------+---------------+-------------------+-------+
+|CFW     |Firms?           |Patch Method     |Supplies Officially           |Available  |Optimization[1]|Focus?             |Notes  |
++--------+-----------------+-----------------+------------------------------+-----------+---------------+-------------------+-------+
+|CakesFW |Dec/Enc (SD)     |Loadable, Fixed  |Sig,Emu(M),Twl,Agb,Sys,Ptc    |Mis,Bck,Mod|Speed/Read     |Devs/Advanced Users| [2]   |
++--------+-----------------+-----------------+------------------------------+-----------+---------------+-------------------+-------+
+|Luma3DS |Enc(NAND) Dec(SD)|Builtin, Dynamic |Sig,Emu(2),Sys,Mod,Bck,Mis,Ptc|N/A        |Giant Mess     |"Noob-proof"       | [3]   |
++--------+-----------------+-----------------+------------------------------+-----------+---------------+-------------------+-------+
+|ReiNAND |Meme(SD)         |Builtin, Dynamic |Sig,Emu,Sys,Mod,Ptc           |N/A        |Readability    |Minimalist         | [4]   |
++--------+-----------------+-----------------+------------------------------+-----------+---------------+-------------------+-------+
+|Corbenik|Dec/Enc (SD)     |Executable       |Sig,Ptc,Bck,Mod,Sys           |Mis        |Read/Speed     |Advanced Devs      | [5]   |
++--------+-----------------+-----------------+------------------------------+-----------+---------------+-------------------+-------+
+|NTR     |N/A              |Executable       |Mis                           |Mis        |Douchebaggery  |Shilling Closed Src| [6]   |
++--------+-----------------+-----------------+------------------------------+-----------+---------------+-------------------+-------+
+
+ * Sig: Signature Patch.
+ * Twl: TWL Signature Patch
+ * Agb: AGB Signature Patch
+ * Ptc: FIRM protection
+ * Emu: Emunand.
+   * (M) - Can have many (unlimited)
+   * (X) - Can have X emunands.
+ * Sys: Can boot sysnand from a9lh.
+ * Mod: Loader replacement
+ * Bck: Load svcBackdoor on 11.0
+ * Mis: Other misc fixes and alterations including:
+   * UNITINFO patch
+   * GBA bios screen
+
+Misc features by CFW:
+ * Cakes:
+   * mid-kid/lgy_cakes
+     * TWL patches
+     * AGB patches
+   * 3ds_injector
+     * You have multiple choices. Wolfvak's, mid-kid's, mine, etc...
+   * Wolfvak/icing
+     * UNITINFO
+     * svcBackdoor (incomplete)
+ * Luma
+   * Loader replacement. CPU speed/language emulation
+
+[1] I'm not just referring to speed; I'm referring to purpose-based optimization. For example;
+    do you want something well documented? Would you choose a slower algorithm that can be more
+    easily debugged at the expense of speed? Would you use preprocessor macros to use multiple?
+
+    CakesFW is Speed/Read. Speed first, keep readability if possible whenever possible. Which is a good
+    approach.
+
+    No, I don't call Luma a complete mess lightly. Go look thorugh the code. You back? Good. Tell me,
+    are the comments helpful and do you understand what the blatant pointer abuse in patches.c is
+    actually ref'ing? No? I had to decode that. It's referencing the exefs's code section. Which was
+    impossible to tell without ten minutes staring at it. Moving on now...
+
+    ReiNAND is cruft-free and well documented. It doesn't do a lot, but what it does do, it does well.
+    Unix philosphy in a nutshell. The only complaint is memekey.
+
+    My focus will always be readability over speed, unless choosing speed and adding additional documentation
+    suffices. As an aside; if you see a single trigraph in my code, please report it. Trigraphs are by design a bug.
+
+[2] CakesFW uses a patch format that has static offsets. They're dynamically loaded and in theory are impervious
+    to updates. In practice, updating patches is a pain and better done by offset patches like Luma3DS/Rei.
+
+    Either way, Cakes allows doing some things the other firmware authors would probably call idiotic; I call them
+    smart since I get a choice. Cakes is lacking a few things from Luma3DS mainly because (in my opinion) the patch
+    format is incapable of accomodating some changes in a sane manner that doesn't require excessive RE or on console
+    one off offset checks.
+
+[3] Luma is a weird beast - "Noob-proof" as github states very perfectly describes it for better or worse. Want to load
+    encrypted SD FIRM? Nope. Load SD firm with SysNAND? Nope. And many options aren't exposed to the user, period. Oh,
+    I suppose there's no legitimate reason to disable firmprot anyways, either. Detection of Emunand #2 tends to be
+    faulty. I have four.
+
+[4] Reisyukaku has fallen behind a bit; his firmware is still in use and rather slim. He does, however, do some fancy
+    ass magic to load a re-encrypted copy (?) of the nintendo firmware using a key refered to as the 'memekey'. How
+    encrypting data differently makes it any less illegal to rehost I have no clue.
+
+[5] Yes, Corbenik currently lacks quite a few things from this graph. On the upside; patches can be implemented
+    externally. I currently have zero interest in implementing reboot; I never need it, having a n3ds. EmuNAND, as
+    well is something I have little interest in (but will happen eventually.)
+
+    As for other bits; Aside from fixing svcBackdoor, I can also inject arbitrary custom services. You're welcome.
+
+[6] Okay, first - I could rant about how NTR is harming us long run for hours. I won't rant for hours, but I will
+    explain some of the rationale here:
+
+    NTR is a secondary CFW; it's designed to be used with a primary one. It consists of a proprietary blob, ntr.bin
+    which loads into memory and takes over the debug service and subsequently the arm11 kernel.
+
+    Normally, the functionality would be useful, but when you consider he's using techniques he hasn't bothered to doc
+    on 3dbrew, you should by now know his goal is lock-in. From BootNTR it's apparent he's a shitty coder[7]. He
+    won't document it, because someone else will create a better optimized version. Hm...that sounds a lot like a
+    certain Japanese company who developed a cons- oh wait.
+
+    The point is; NTR is not your friend. Stop using it.
+
+[7] Tell me that giant switch statement couldn't be implemented as a dynamic patcher on the 3DS itself, considering
+    all of the python offset extractor could be rewritten in C. Dare you. He also can't be bothered to port to 10.4
+    NATIVE_FIRM even though there's literally no difference that should break NTR.
diff --git a/doc/nonos.md b/doc/nonos.md
new file mode 100644 (file)
index 0000000..ba3d6bc
--- /dev/null
@@ -0,0 +1,23 @@
+Things I won't implement
+==========================
+
+ * Memekey Support.
+
+This is a ReiNAND only thing; get over it.
+
+ * Loading firmware from sysnand.
+
+IMHO, this is a bad idea for many reasons.
+
+Corbenik is for people who know what they're doing first and foremost, so if getting a firmware is too hard, you're not the intended audience. Please use another CFW.
+
+
+Not bugs
+=========
+
+ * CETK decryption
+
+No, I can't fix this on a9lh. Here's how to do it; boot another CFW, go into system settings, then boot corbenik. You'll get the keys you need. This works with Cakes as well.
+
+Another thing to know is only NATIVE_FIRM is required to boot, so you can boot Corbenik without AGB and TWL suceeding, reboot from settings and they'll decrypt.
+
diff --git a/external/Makefile b/external/Makefile
new file mode 100644 (file)
index 0000000..087842f
--- /dev/null
@@ -0,0 +1,16 @@
+.PHONY: all copyout
+all: loader
+       mkdir -p ../out/corbenik/module
+       cp loader/loader.cxi ../out/corbenik/module/loader.cxi
+
+.PHONY: clean
+clean: clean_loader
+       rm -rf ../out/corbenik/module
+
+.PHONY: loader
+loader:
+       make -C loader
+
+.PHONY: clean_loader
+clean_loader:
+       make -C loader clean
diff --git a/external/loader/LICENSE b/external/loader/LICENSE
new file mode 100644 (file)
index 0000000..5fcfe11
--- /dev/null
@@ -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.
diff --git a/external/loader/Makefile b/external/loader/Makefile
new file mode 100644 (file)
index 0000000..43eb971
--- /dev/null
@@ -0,0 +1,164 @@
+#---------------------------------------------------------------------------------
+.SUFFIXES:
+#---------------------------------------------------------------------------------
+
+PATH := $(PATH):$(DEVKITARM)/bin
+
+ifeq ($(strip $(DEVKITARM)),)
+$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
+endif
+
+TOPDIR ?= $(CURDIR)
+MAKEROM ?= makerom
+PYTHON ?= python3
+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         :=      loader
+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 -Os -mword-relocations \
+                       -fomit-frame-pointer -ffunction-sections -fdata-sections -fshort-wchar \
+                       $(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
+#---------------------------------------------------------------------------------------
diff --git a/external/loader/README.md b/external/loader/README.md
new file mode 100644 (file)
index 0000000..e985160
--- /dev/null
@@ -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).
diff --git a/external/loader/loader.rsf b/external/loader/loader.rsf
new file mode 100644 (file)
index 0000000..dfeeb0e
--- /dev/null
@@ -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
\ No newline at end of file
diff --git a/external/loader/source/config.h b/external/loader/source/config.h
new file mode 100644 (file)
index 0000000..aee78c0
--- /dev/null
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <stdint.h>
+
+struct config_file {
+    unsigned int config_ver;
+    unsigned int firm_ver;
+    uint8_t firm_console;
+    uint32_t emunand_location;
+
+    unsigned int autoboot_enabled: 1;
+       unsigned int n3ds_clock: 1;
+       unsigned int n3ds_l2: 1;
+       unsigned int language_emu: 1;
+
+       /* The cakes_count and cakes data
+          is excluded because loader has
+       no use for it. */
+} __attribute__((packed));
+
diff --git a/external/loader/source/exheader.h b/external/loader/source/exheader.h
new file mode 100755 (executable)
index 0000000..be23527
--- /dev/null
@@ -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;
diff --git a/external/loader/source/fsldr.c b/external/loader/source/fsldr.c
new file mode 100644 (file)
index 0000000..e90491c
--- /dev/null
@@ -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];
+}
diff --git a/external/loader/source/fsldr.h b/external/loader/source/fsldr.h
new file mode 100644 (file)
index 0000000..115a745
--- /dev/null
@@ -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);
diff --git a/external/loader/source/fsreg.c b/external/loader/source/fsreg.c
new file mode 100644 (file)
index 0000000..d8301bb
--- /dev/null
@@ -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];
+}
diff --git a/external/loader/source/fsreg.h b/external/loader/source/fsreg.h
new file mode 100644 (file)
index 0000000..c2de1ed
--- /dev/null
@@ -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);
diff --git a/external/loader/source/ifile.c b/external/loader/source/ifile.c
new file mode 100644 (file)
index 0000000..5faf2ae
--- /dev/null
@@ -0,0 +1,66 @@
+#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;
+}
\ No newline at end of file
diff --git a/external/loader/source/ifile.h b/external/loader/source/ifile.h
new file mode 100644 (file)
index 0000000..bf2ae7e
--- /dev/null
@@ -0,0 +1,15 @@
+#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);
\ No newline at end of file
diff --git a/external/loader/source/loader.c b/external/loader/source/loader.c
new file mode 100644 (file)
index 0000000..9698b8d
--- /dev/null
@@ -0,0 +1,592 @@
+#include <3ds.h>
+#include "memory.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
+  patchCode(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(&notid);
+  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()
+{
+  __appExit();
+  __sync_fini();
+  svcExitProcess();
+}
+void initSystem()
+{
+  __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 == (int)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;
+}
diff --git a/external/loader/source/memory.c b/external/loader/source/memory.c
new file mode 100644 (file)
index 0000000..ba55303
--- /dev/null
@@ -0,0 +1,11 @@
+#include "memory.h"
+
+void memcpy(void *dest, const void *src, u32 size)
+{
+    u8 *destc = (u8 *)dest;
+    const u8 *srcc = (const u8 *)src;
+
+    for(u32 i = 0; i < size; i++)
+        destc[i] = srcc[i];
+}
+
diff --git a/external/loader/source/memory.h b/external/loader/source/memory.h
new file mode 100644 (file)
index 0000000..76d58c7
--- /dev/null
@@ -0,0 +1,5 @@
+#pragma once
+
+#include <3ds/types.h>
+
+void memcpy(void *dest, const void *src, u32 size);
diff --git a/external/loader/source/patcher.c b/external/loader/source/patcher.c
new file mode 100644 (file)
index 0000000..35ee5c8
--- /dev/null
@@ -0,0 +1,529 @@
+#include <3ds.h>
+#include "memory.h"
+#include "patcher.h"
+#include "ifile.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 255
+#define _MAX_LFN 255
+#endif
+#include "config.h"
+
+static int memcmp(const void *buf1, const void *buf2, u32 size)
+{
+    const u8 *buf1c = (const u8 *)buf1;
+    const u8 *buf2c = (const u8 *)buf2;
+
+    for(u32 i = 0; i < size; i++)
+    {
+        int cmp = buf1c[i] - buf2c[i];
+        if(cmp) return cmp;
+    }
+
+    return 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
+    u32 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 patchMemory(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) break;
+
+        size -= at + patSize;
+        start = found + patSize;
+    }
+
+    return i;
+}
+
+static inline size_t strnlen(const char *string, size_t maxlen)
+{
+    size_t size;
+
+    for(size = 0; *string && size < maxlen; string++, size++);
+
+    return size;
+}
+
+static int fileOpen(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 u32 secureInfoExists(void)
+{
+    static u32 secureInfoExists = 0;
+
+    if(!secureInfoExists)
+    {
+        IFile file;
+        if(!fileOpen(&file, ARCHIVE_NAND_RW, "/sys/SecureInfo_C", FS_OPEN_READ))
+        {
+            secureInfoExists = 1;
+            IFile_Close(&file);
+        }
+    }
+
+    return secureInfoExists;
+}
+
+static unsigned int config_ver = 4;
+static struct config_file config;
+static int failed_load_config = 1;
+void load_config() {
+    // Make sure we don't get random values if the config file doesn't load
+       for(int i=0;i<sizeof(struct config_file);i++) ((char*)&config)[i]=0;
+
+       IFile file;
+    u64 total;
+
+       // Open file.
+    if (!fileOpen(&file, ARCHIVE_SDMC, "/corbenik/config.dat", FS_OPEN_READ)) {
+               // Failed to open.
+               failed_load_config = 1;
+        return;
+    }
+
+       // Read file.
+    if(!IFile_Read(&file, &total, &config, sizeof(struct config_file))) {
+               // Failed to read.
+               failed_load_config = 1;
+               for(int i=0;i<sizeof(struct config_file);i++) ((char*)&config)[i]=0;
+               return;
+       }
+
+    if (config.config_ver != config_ver) {
+               // Invalid version.
+               failed_load_config = 1;
+               for(int i=0;i<sizeof(struct config_file);i++) ((char*)&config)[i]=0;
+        return;
+    }
+
+       IFile_Close(&file);
+       // Loaded okay.
+}
+
+static int loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
+{
+    /* Here we look for "/cfw/locale/[u64 titleID in hex, uppercase].txt"
+       If it exists it should contain, for example, "EUR IT" */
+
+    char path[] = "/corbenik/locales/0000000000000000.txt";
+
+    u32 i = strlen(path) - 21;
+
+    while(progId > 0)
+    {
+        static const char hexDigits[] = "0123456789ABCDEF";
+        path[i--] = hexDigits[(u32)(progId & 0xF)];
+        progId >>= 4;
+    }
+
+    IFile file;
+    Result ret = fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ);
+    if(R_SUCCEEDED(ret))
+    {
+        char buf[6];
+        u64 total;
+
+        ret = IFile_Read(&file, &total, buf, 6);
+        IFile_Close(&file);
+
+        if(!R_SUCCEEDED(ret) || total < 6) return -1;
+
+        for(u32 i = 0; i < 7; ++i)
+        {
+            static const char *regions[] = {"JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"};
+
+            if(memcmp(buf, regions[i], 3) == 0)
+            {
+                *regionId = (u8)i;
+                break;
+            }
+        }
+
+        for(u32 i = 0; i < 12; ++i)
+        {
+            static const char *languages[] = {"JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"};
+
+            if(memcmp(buf + 4, languages[i], 2) == 0)
+            {
+                *languageId = (u8)i;
+                break;
+            }
+        }
+    }
+
+    return ret;
+}
+
+static u8 *getCfgOffsets(u8 *code, u32 size, u32 *CFGUHandleOffset)
+{
+    /* HANS:
+       Look for error code which is known to be stored near cfg:u handle
+       this way we can find the right candidate
+       (handle should also be stored right after end of candidate function) */
+
+    u32 n = 0,
+        possible[24];
+
+    for(u8 *pos = code + 4; n < 24 && pos < code + size - 4; pos += 4)
+    {
+        if(*(u32 *)pos == 0xD8A103F9)
+        {
+            for(u32 *l = (u32 *)pos - 4; n < 24 && l < (u32 *)pos + 4; l++)
+                if(*l <= 0x10000000) possible[n++] = *l;
+        }
+    }
+
+    for(u8 *CFGU_GetConfigInfoBlk2_endPos = code; CFGU_GetConfigInfoBlk2_endPos < code + size - 8; CFGU_GetConfigInfoBlk2_endPos += 4)
+    {
+        static const u32 CFGU_GetConfigInfoBlk2_endPattern[] = {0xE8BD8010, 0x00010082};
+
+        //There might be multiple implementations of GetConfigInfoBlk2 but let's search for the one we want
+        u32 *cmp = (u32 *)CFGU_GetConfigInfoBlk2_endPos;
+
+        if(cmp[0] == CFGU_GetConfigInfoBlk2_endPattern[0] && cmp[1] == CFGU_GetConfigInfoBlk2_endPattern[1])
+        {
+            *CFGUHandleOffset = *((u32 *)CFGU_GetConfigInfoBlk2_endPos + 2);
+
+            for(u32 i = 0; i < n; i++)
+                if(possible[i] == *CFGUHandleOffset) return CFGU_GetConfigInfoBlk2_endPos;
+
+            CFGU_GetConfigInfoBlk2_endPos += 4;
+        }
+    }
+
+    return NULL;
+}
+
+static void patchCfgGetLanguage(u8 *code, u32 size, u8 languageId, u8 *CFGU_GetConfigInfoBlk2_endPos)
+{
+    u8 *CFGU_GetConfigInfoBlk2_startPos; //Let's find STMFD SP (there might be a NOP before, but nevermind)
+
+    for(CFGU_GetConfigInfoBlk2_startPos = CFGU_GetConfigInfoBlk2_endPos - 4;
+        CFGU_GetConfigInfoBlk2_startPos >= code && *((u16 *)CFGU_GetConfigInfoBlk2_startPos + 1) != 0xE92D;
+        CFGU_GetConfigInfoBlk2_startPos -= 2);
+
+    for(u8 *languageBlkIdPos = code; languageBlkIdPos < code + size; languageBlkIdPos += 4)
+    {
+        if(*(u32 *)languageBlkIdPos == 0xA0002)
+        {
+            for(u8 *instr = languageBlkIdPos - 8; instr >= languageBlkIdPos - 0x1008 && instr >= code + 4; instr -= 4) //Should be enough
+            {
+                if(instr[3] == 0xEB) //We're looking for BL
+                {
+                    u8 *calledFunction = instr;
+                    u32 i = 0,
+                        found;
+
+                    do
+                    {
+                        u32 low24 = (*(u32 *)calledFunction & 0x00FFFFFF) << 2;
+                        u32 signMask = (u32)(-(low24 >> 25)) & 0xFC000000; //Sign extension
+                        s32 offset = (s32)(low24 | signMask) + 8;          //Branch offset + 8 for prefetch
+
+                        calledFunction += offset;
+
+                        found = calledFunction >= CFGU_GetConfigInfoBlk2_startPos - 4 && calledFunction <= CFGU_GetConfigInfoBlk2_endPos;
+                        i++;
+                    }
+                    while(i < 2 && !found && calledFunction[3] == 0xEA);
+
+                    if(found)
+                    {
+                        *((u32 *)instr - 1)  = 0xE3A00000 | languageId; // mov    r0, sp                 => mov r0, =languageId
+                        *(u32 *)instr        = 0xE5CD0000;              // bl     CFGU_GetConfigInfoBlk2 => strb r0, [sp]
+                        *((u32 *)instr + 1)  = 0xE3B00000;              // (1 or 2 instructions)         => movs r0, 0             (result code)
+
+                        //We're done
+                        return;
+                    }
+                }
+            }
+        }
+    }
+}
+
+static void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHandleOffset)
+{
+    for(u8 *cmdPos = code; cmdPos < code + size - 28; cmdPos += 4)
+    {
+        static const u32 cfgSecureInfoGetRegionCmdPattern[] = {0xEE1D4F70, 0xE3A00802, 0xE5A40080};
+
+        u32 *cmp = (u32 *)cmdPos;
+
+        if(cmp[0] == cfgSecureInfoGetRegionCmdPattern[0] && cmp[1] == cfgSecureInfoGetRegionCmdPattern[1] &&
+           cmp[2] == cfgSecureInfoGetRegionCmdPattern[2] && *((u16 *)cmdPos + 7) == 0xE59F &&
+           *(u32 *)(cmdPos + 20 + *((u16 *)cmdPos + 6)) == CFGUHandleOffset)
+        {
+            *((u32 *)cmdPos + 4) = 0xE3A00000 | regionId; // mov    r0, =regionId
+            *((u32 *)cmdPos + 5) = 0xE5C40008;            // strb   r0, [r4, 8]
+            *((u32 *)cmdPos + 6) = 0xE3B00000;            // movs   r0, 0            (result code) ('s' not needed but nvm)
+            *((u32 *)cmdPos + 7) = 0xE5840004;            // str    r0, [r4, 4]
+
+            //The remaining, not patched, function code will do the rest for us
+            break;
+        }
+    }
+}
+
+void patchCode(u64 progId, u8 *code, u32 size)
+{
+    load_config(); // Load cakes.dat
+
+    switch(progId)
+    {
+        case 0x0004003000008F02LL: // USA Menu
+        case 0x0004003000008202LL: // EUR Menu
+        case 0x0004003000009802LL: // JPN Menu
+        case 0x000400300000A102LL: // CHN Menu
+        case 0x000400300000A902LL: // KOR Menu
+        case 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 SMDH region checks
+            patchMemory(code, size,
+                regionFreePattern,
+                sizeof(regionFreePattern), -16,
+                regionFreePatch,
+                sizeof(regionFreePatch), 1
+            );
+
+            break;
+        }
+
+        case 0x0004013000002C02LL: // NIM
+        {
+            static const u8 blockAutoUpdatesPattern[] = {
+                0x25, 0x79, 0x0B, 0x99
+            };
+            static const u8 blockAutoUpdatesPatch[] = {
+                0xE3, 0xA0
+            };
+            static const u8 skipEshopUpdateCheckPattern[] = {
+                0x30, 0xB5, 0xF1, 0xB0
+            };
+            static const u8 skipEshopUpdateCheckPatch[] = {
+                0x00, 0x20, 0x08, 0x60, 0x70, 0x47
+            };
+
+            //Block silent auto-updates
+            patchMemory(code, size,
+                blockAutoUpdatesPattern,
+                sizeof(blockAutoUpdatesPattern), 0,
+                blockAutoUpdatesPatch,
+                sizeof(blockAutoUpdatesPatch), 1
+            );
+
+            //Skip update checks to access the EShop
+            patchMemory(code, size,
+                skipEshopUpdateCheckPattern,
+                sizeof(skipEshopUpdateCheckPattern), 0,
+                skipEshopUpdateCheckPatch,
+                sizeof(skipEshopUpdateCheckPatch), 1
+            );
+
+            break;
+        }
+
+        case 0x0004013000003202LL: // FRIENDS
+        {
+            static const u8 fpdVerPattern[] = {
+                0xE0, 0x1E, 0xFF, 0x2F, 0xE1, 0x01, 0x01, 0x01
+            };
+
+            static const u8 fpdVerPatch = 0x05;
+
+            //Allow online access to work with old friends modules
+            patchMemory(code, size,
+                fpdVerPattern,
+                sizeof(fpdVerPattern), 9,
+                &fpdVerPatch,
+                sizeof(fpdVerPatch), 1
+            );
+
+            break;
+        }
+
+        case 0x0004001000021000LL: // USA MSET
+        case 0x0004001000020000LL: // JPN MSET
+        case 0x0004001000022000LL: // EUR MSET
+        case 0x0004001000026000LL: // CHN MSET
+        case 0x0004001000027000LL: // KOR MSET
+        case 0x0004001000028000LL: // TWN MSET
+        {
+            static const u16 verPattern[] = u"Ver.";
+
+            //Patch Ver. string
+            patchMemory(code, size,
+                verPattern,
+                sizeof(verPattern) - sizeof(u16), 0,
+                u".hax",
+                sizeof(verPattern) - sizeof(u16), 1
+            );
+            break;
+        }
+
+        case 0x0004013000008002LL: // NS
+        {
+            static const u8 stopCartUpdatesPattern[] = {
+                0x0C, 0x18, 0xE1, 0xD8
+            };
+            static const u8 stopCartUpdatesPatch[] = {
+                0x0B, 0x18, 0x21, 0xC8
+            };
+
+            //Disable updates from foreign carts (makes carts region-free)
+            patchMemory(code, size, 
+                stopCartUpdatesPattern, 
+                sizeof(stopCartUpdatesPattern), 0, 
+                stopCartUpdatesPatch,
+                sizeof(stopCartUpdatesPatch), 2
+            );
+
+                       if (!failed_load_config) {
+                   u32 cpuSetting = 0;
+                               cpuSetting += config.n3ds_clock ? 0x1 : 0x0;
+                               cpuSetting += config.n3ds_l2    ? 0x2 : 0x0;
+
+                   if(cpuSetting)
+               {
+                       static const u8 cfgN3dsCpuPattern[] = {
+                       0x00, 0x40, 0xA0, 0xE1, 0x07, 0x00
+                       };
+
+                       u32 *cfgN3dsCpuLoc = (u32 *)memsearch(code, cfgN3dsCpuPattern, size, sizeof(cfgN3dsCpuPattern));
+
+                   //Patch N3DS CPU Clock and L2 cache setting
+                       if(cfgN3dsCpuLoc != NULL)
+                   {
+                           *(cfgN3dsCpuLoc + 1) = 0xE1A00000;
+                       *(cfgN3dsCpuLoc + 8) = 0xE3A00000 | cpuSetting;
+                       }
+               }
+                       }
+            break;
+        }
+
+        case 0x0004013000001702LL: // CFG
+        {
+            static const u8 secureinfoSigCheckPattern[] = {
+                0x06, 0x46, 0x10, 0x48, 0xFC
+            };
+            static const u8 secureinfoSigCheckPatch[] = {
+                0x00, 0x26
+            };
+
+            //Disable SecureInfo signature check
+            patchMemory(code, size,
+                secureinfoSigCheckPattern,
+                sizeof(secureinfoSigCheckPattern), 0,
+                secureinfoSigCheckPatch,
+                sizeof(secureinfoSigCheckPatch), 1
+            );
+
+            if(secureInfoExists())
+            {
+                static const u16 secureinfoFilenamePattern[] = u"SecureInfo_";
+                static const u16 secureinfoFilenamePatch[] = u"C";
+
+                //Use SecureInfo_C
+                patchMemory(code, size,
+                    secureinfoFilenamePattern,
+                    sizeof(secureinfoFilenamePattern) - sizeof(u16),
+                    sizeof(secureinfoFilenamePattern) - sizeof(u16),
+                    secureinfoFilenamePatch,
+                    sizeof(secureinfoFilenamePatch) - sizeof(u16), 2
+                );
+            }
+
+            break;
+        }
+        default:
+               {
+            if(!failed_load_config && config.language_emu)
+            {
+                u32 tidHigh = (progId & 0xFFFFFFF000000000LL) >> 0x24;
+
+                if(tidHigh == 0x0004000)
+                {
+                    //Language emulation
+                    u8 regionId = 0xFF,
+                       languageId = 0xFF;
+
+                    if(R_SUCCEEDED(loadTitleLocaleConfig(progId, &regionId, &languageId)))
+                    {
+                        u32 CFGUHandleOffset;
+
+                        u8 *CFGU_GetConfigInfoBlk2_endPos = getCfgOffsets(code, size, &CFGUHandleOffset);
+
+                        if(CFGU_GetConfigInfoBlk2_endPos != NULL)
+                        {
+                            if(languageId != 0xFF)
+                                patchCfgGetLanguage(code, size, languageId, CFGU_GetConfigInfoBlk2_endPos);
+                            if(regionId != 0xFF)
+                                patchCfgGetRegion(code, size, regionId, CFGUHandleOffset);
+                        }
+                    }
+                }
+            }
+
+            break;
+        }
+    }
+}
diff --git a/external/loader/source/patcher.h b/external/loader/source/patcher.h
new file mode 100644 (file)
index 0000000..4bb48ea
--- /dev/null
@@ -0,0 +1,5 @@
+#pragma once
+
+#include <3ds/types.h>
+
+void patchCode(u64 progId, u8 *code, u32 size);
\ No newline at end of file
diff --git a/external/loader/source/pxipm.c b/external/loader/source/pxipm.c
new file mode 100644 (file)
index 0000000..d85d58f
--- /dev/null
@@ -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];
+}
diff --git a/external/loader/source/pxipm.h b/external/loader/source/pxipm.h
new file mode 100644 (file)
index 0000000..e9d1feb
--- /dev/null
@@ -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);
diff --git a/external/loader/source/srvsys.c b/external/loader/source/srvsys.c
new file mode 100644 (file)
index 0000000..1bd05ed
--- /dev/null
@@ -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];
+}
diff --git a/external/loader/source/srvsys.h b/external/loader/source/srvsys.h
new file mode 100644 (file)
index 0000000..7c1c0a8
--- /dev/null
@@ -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);
index e6b9a0049692508af365948216ab6d9f6dfdce17..0b692daf0b3674efef2e432f58dcb3e1bd0b97c2 100644 (file)
@@ -63,7 +63,8 @@ void load_config() {
         }
     }
 
-    fprintf(BOTTOM_SCREEN, "Config file loaded.\n");
+       if (!config.options[OPTION_SILENCE])
+       fprintf(BOTTOM_SCREEN, "Config file loaded.\n");
 }
 
 void save_config() {
index 33843bf7f505b6ab95f90896d8ec6f1956daa405..28c3e416157b4d577283d18d9d0dda53bf40176f 100644 (file)
@@ -3,33 +3,32 @@
 #include "input.h"
 #include "config.h"
 
-void init_system() {
-}
-
 int menu_handler();
 
 int doing_autoboot = 0;
+void shut_up();
 
 int main() {
     if (fmount()) {
         // Failed to mount SD. Bomb out.
         fprintf(BOTTOM_SCREEN, "%pFailed to mount SD card.\n", COLOR(RED, BLACK));
-    } else {
-        fprintf(BOTTOM_SCREEN, "Mounted SD card.\n");
     }
 
     load_config(); // Load configuration.
 
-    load_firms();
-
-       // Autoboot, and not R?
+       // Autoboot. Non-standard code path.
        if (config.options[OPTION_AUTOBOOT] && !(HID_PAD & BUTTON_R)) {
+               if (config.options[OPTION_SILENCE])
+                       shut_up();
+               load_firms();
                doing_autoboot = 1;
                boot_cfw(); // Just boot shit.
        }
 
     int in_menu = 1;
 
+    load_firms();
+
     while(in_menu) {
         in_menu = menu_handler();
     }
index 312878e2ca510a51f2dda324d09e0123a9e40945..a59f2a3c0f42724971a6d3947e8ba9ebc6ac41c1 100644 (file)
@@ -132,15 +132,23 @@ void draw_character(uint8_t* screen, const char character, const unsigned int bu
 
 unsigned char color_top = 0xf0;
 unsigned char color_bottom = 0xf0;
+int kill_output = 0;
+
+void shut_up() {
+       kill_output = !kill_output;
+}
 
 void putc(void* buf, const int c) {
     if (buf == stdout || buf == stderr) {
+               if (kill_output)
+                       return;
+
                unsigned int width  = 0;
                _UNUSED unsigned int height = 0;
-       unsigned int *cursor_x;
-       unsigned int *cursor_y;
-       char *colorbuf;
-       char *strbuf;
+       unsigned int *cursor_x = NULL;
+       unsigned int *cursor_y = NULL;
+       char *colorbuf = NULL;
+       char *strbuf = NULL;
 
                unsigned char* color = NULL;
 
@@ -316,10 +324,7 @@ void fflush(void* channel) {
        }
 }
 
-void fprintf(void* channel, const char* format, ...) {
-    va_list ap;
-    va_start( ap, format );
-
+void vfprintf(void* channel, const char* format, va_list ap ) {
     char *ref = (char*)format;
 
        unsigned char* color;
@@ -423,8 +428,14 @@ check_format:
         ++ref;
     }
 
-    va_end( ap );
-
     fflush(channel);
 }
 
+void fprintf(void* channel, const char* format, ...) {
+    va_list ap;
+    va_start( ap, format );
+
+       vfprintf( channel, format, ap );
+
+    va_end( ap );
+}
index 8a9bca2130ccea3404ca463097c56af0d4c444dc..de1a361ae0e02ae94624335684eccb12d484f36c 100644 (file)
@@ -14,7 +14,7 @@ dir_build  := build
 dir_out    := out
 
 ASFLAGS := -mlittle-endian -mcpu=arm946e-s -march=armv5te
-CFLAGS  := -MMD -MP -Wall -Wextra -Werror -O0 $(ASFLAGS) -fno-builtin -std=c11 -marm
+CFLAGS  := -MMD -MP -Wall -Wextra -Werror -O0 $(ASFLAGS) -fno-builtin -std=c11
 FLAGS   := dir_out=$(abspath $(dir_out)) --no-print-directory
 LDFLAGS := -nostdlib -Wl,-z,defs -lgcc