before it starts. For example, you can patch `menu` to skip region checks and
have region free game launching directly from the home menu.
-This currently requires recompilation which makes it less than ideal.
+There is also support for SDMC read/write (not found in original loader
+implementation) which means that information can be logged to SD.
-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 will be a patch system that supports easy loading of
-patches from the SD card.
+## About this fork
-## Changes I've made
+A lot of the 'disassembled' looking code has been rewritten for readability
+(notable the lzss decompressor), and some cruft was cleaned out. Notably,
+IFile increased the cxi size by two (!) pages, and therefore has been removed,
+as it offers nothing over direct use of FSFILE and FSLDR.
-A lot of the 'disassembled' looking code has been rewritten for readability, and
-many cruft-ish artifacts of it have been cleaned up. Some wrapper functions
-have also been rewritten out of the code, and anything nigh-unreadable and
-non-rewritable has been documented when I managed to figure out what it does.
+This version of loader is capable of logging to the filesystem through added
+write support. Tecnically, all the work for this was already in place, but IFile
+didn't allow accessing this functionality. This makes debugging much easier,
+since you can now actually figure out what went wrong.
-At the moment there is also experimental support for resizing segments before
-loading the executable. This means that segments can be appended to. Notably,
-this allows tacking on code to the end.
+Experimental support for resizing segments before loading the executable was
+added. This means that segments can be appended to. Notably, this allows tacking
+code onto the end.
-The text, data, and ro segments are handled separately to streamline the new
-segment resizing and limit search space to speed things up a tad. Why search
-text, data and ro (big) when you know it is in text?
+The text, data, and ro segments are now handled separately to speed things up
+marginally, since if you know a patch is applied to text there's no reason to
+search the data segment, etc.
## Imported changes from other 3ds_injector forks
-I updated it to the latest git ctrulib (in which FS_Archive is a u64,
-not a struct.) @TuxSH did the work before me, although it required manual
-conflict merges since my tree differs a LOT from both @yifanlu and his
-version.
+This was updated to the latest git ctrulib (in which FS_Archive typing has
+changed to allow mountpoints.)
+
+@TuxSH did this work before me, although I had to implement changes
+manually due to my tree sharing almost nothing at this point with upstream or
+Luma. It did serve as a useful reference, though. :)
## Build
You need a working 3DS build environment with a fairly recent copy of devkitARM,
ctrulib, and makerom.
-This is not intended to be used with anything but corbenik, so please don't use
-binaries of this with any other CFW. For devs - message me if there's any changes
-you want help merging. I'll be glad to help. I'm not into anti-competitive
-behavior. ;P
+This will automatically be built by corbenik's top-level makefile, so you shouldn't
+need to monkey around in here if you aren't making changes.
+
+## Misc
+
+This is not intended to be used with anything but corbenik due to specific use of
+structures incompatible with other CFW, so don't attempt to inject this with other
+CFWs. At best, it doesn't work. At worst, you can't boot without removing it.
+
+For devs - message me if there's any changes you want help merging to your fork.
+I'll be glad to help, either to explain what has been done here or to port changes.
+I'm not into anti-competitive behavior, since we're all just trying to make the 3DS
+better as a whole. ;P
#ifndef __INTERNAL_H
#define __INTERNAL_H
-// This is a GCC builtin, and so there's no need to carry an implementation
-// here.
+// These are libc builtins, so there's no need to carry an implementation here.
void* memcpy(void* dest, const void* src, size_t len);
+size_t strlen(const char* string);
#endif
#include "srvsys.h"
#include "lzss.h"
#include "internal.h"
+#include "logger.h"
// TODO - a lot of this is unecessarily verbose and shitty. Clean it up to be
// tidy.
u64 progid;
u32 text_grow, data_grow, ro_grow;
+ openLogger();
+
// 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);
return MAKERESULT(RL_PERMANENT, RS_INVALIDARG, 1, 2);
}
+ logstr("validated params\n");
+
load_config(); // First order of business - we need the config file.
// Check and set the CPU mode. Possible values: 0 - Keep o3ds speed, 1 -
&codeset, &codesetinfo, (void*)shared_addr.text_addr,
(void*)shared_addr.ro_addr, (void*)shared_addr.data_addr);
if (res >= 0) {
+ closeLogger();
+
res =
svcCreateProcess(process, codeset,
g_exheader.arm11kernelcaps.descriptors, count);
--- /dev/null
+#include <3ds.h>
+#include "patcher.h"
+#include "fsldr.h"
+#include "internal.h"
+#include "memory.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 255
+#define _MAX_LFN 255
+#endif
+#include "config.h"
+#include "../../../source/patch_format.h"
+
+#include "patch/patch.h"
+
+Handle log_file_hdl;
+int logger_is_initd = 0;
+
+void openLogger() {
+ Result r;
+
+ if (logger_is_initd)
+ return;
+
+ r = fileOpen(&log_file_hdl, ARCHIVE_SDMC, "/corbenik/loader.log", FS_OPEN_WRITE|FS_OPEN_READ|FS_OPEN_CREATE);
+
+ if (R_FAILED(r)) {
+ logger_is_initd = -1;
+ }
+
+ logger_is_initd = 1;
+}
+
+void logstr(const char* str) {
+ if (logger_is_initd == -1)
+ return; // Errored during init. Don't bother.
+
+ u32 len = strlen(str);
+ u64 size;
+ u32 wrote;
+ Result r;
+
+ // Get current size.
+ r = FSFILE_GetSize(log_file_hdl, &size);
+ if (R_FAILED(r))
+ return;
+
+ // Expand file size.
+ r = FSFILE_SetSize(log_file_hdl, size+len);
+ if (R_FAILED(r))
+ return;
+
+ // Write data.
+ FSFILE_Write(log_file_hdl, &wrote, size, str, len, 0);
+}
+
+void closeLogger() {
+ FSFILE_Close(log_file_hdl);
+ logger_is_initd = 0;
+}
--- /dev/null
+#ifndef __LOGGER_H
+#define __LOGGER_H
+
+void openLogger();
+void logstr(const char* str);
+void closeLogger();
+
+#endif
--- /dev/null
+#include <3ds.h>
+#include "patcher.h"
+#include "fsldr.h"
+#include "internal.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 255
+#define _MAX_LFN 255
+#endif
+#include "config.h"
+#include "../../../source/patch_format.h"
+
+#include "patch/patch.h"
+
+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
+u8*
+memfind(u8* startPos, u32 size, const void* pattern, 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;
+}
+
+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 = memfind(start, size, pattern, patSize);
+
+ if (found == NULL)
+ break;
+
+ // FIXME - This is throwing on Werror.
+ memcpy(found + offset, replace, repSize);
+
+ u32 at = (u32)(found - start);
+
+ if (at + patSize > size)
+ break;
+
+ size -= at + patSize;
+ start = found + patSize;
+ }
+
+ return i;
+}
+
+size_t
+strnlen(const char* string, size_t maxlen)
+{
+ size_t size;
+
+ for (size = 0; *string && size < maxlen; string++, size++);
+
+ return size;
+}
--- /dev/null
+#ifndef __MEMORY_H
+#define __MEMORY_H
+
+int memcmp(const void* buf1, const void* buf2, u32 size);
+u8* memfind(u8* startPos, u32 size, const void* pattern, u32 patternSize);
+u32 patchMemory(u8* start, u32 size, const void* pattern, u32 patSize, int offset,
+ const void* replace, u32 repSize, u32 count);
+size_t strnlen(const char* string, size_t maxlen);
+
+#endif
patchMemory(code, size, stopCartUpdatesPattern,
sizeof(stopCartUpdatesPattern), 0, stopCartUpdatesPatch,
sizeof(stopCartUpdatesPatch), 2);
+
+ logstr("disable_cart_updates\n");
}
sizeof(skipEshopUpdateCheckPattern), 0,
skipEshopUpdateCheckPatch, sizeof(skipEshopUpdateCheckPatch),
1);
+
+ logstr("disable_eshop_updates\n");
}
patchMemory(code, size, blockAutoUpdatesPattern,
sizeof(blockAutoUpdatesPattern), 0, blockAutoUpdatesPatch,
sizeof(blockAutoUpdatesPatch), 1);
+
+ logstr("disable_nim_updates\n");
}
// Allow online access to work with old friends modules
patchMemory(code, size, fpdVerPattern, sizeof(fpdVerPattern), 9,
&fpdVerPatch, sizeof(fpdVerPatch), 1);
+
+ logstr("fake_friends_version\n");
}
// Patch Ver. string
patchMemory(code, size, verPattern, sizeof(verPattern) - sizeof(u16), 0,
verPatch, sizeof(verPatch) - sizeof(u16), 1);
+
+ logstr("settings_string\n");
}
#include <3ds.h>
#include "../patcher.h"
+#include "../memory.h"
+#include "../logger.h"
#ifndef PATH_MAX
#define PATH_MAX 255
// Patch SMDH region checks
patchMemory(code, size, regionFreePattern, sizeof(regionFreePattern), -16,
regionFreePatch, sizeof(regionFreePatch), 1);
+
+ logstr("region_patch\n");
}
patchMemory(code, size, sha256ChecksPattern2, sizeof(sha256ChecksPattern2),
0, stub, sizeof(stub), 1);
+
+ logstr("ro_sigpatch\n");
}
patchMemory(code, size, secureinfoSigCheckPattern,
sizeof(secureinfoSigCheckPattern), 0, secureinfoSigCheckPatch,
sizeof(secureinfoSigCheckPatch), 1);
+
+ logstr("secureinfo_sigpatch\n");
}
#include "patcher.h"
#include "fsldr.h"
#include "internal.h"
+#include "memory.h"
+#include "logger.h"
#ifndef PATH_MAX
#define PATH_MAX 255
#include "patch/patch.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*
-memfind(u8* startPos, u32 size, const void* pattern, 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;
-}
-
-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 = memfind(start, size, pattern, patSize);
-
- if (found == NULL)
- break;
-
- // FIXME - This is throwing on Werror.
- 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
+int
fileOpen(Handle* file, FS_ArchiveID id, const char* path, int flags)
{
FS_Path apath;
failed_load_config = 0;
+ logstr("loaded config file\n");
+
return;
}
break;
}
}
+
+ logstr("langemu cfg applied\n");
+ logstr(path);
+ logstr("\n");
}
return ret;
}
0xE3B00000; // (1 or 2 instructions) => movs
// r0, 0 (result code)
+ logstr("patched language\n");
+
// We're done
return;
}
break;
}
}
+
+ logstr("patched region\n");
}
static void
}
}
}
+
+ logstr("set up langemu\n");
}
void
void load_config();
-u32 patchMemory(u8* start, u32 size, const void* pattern, u32 patSize,
- int offset, const void* replace, u32 repSize, u32 count);
+int fileOpen(Handle* file, FS_ArchiveID id, const char* path, int flags);
u8 get_cpumode(u64 progId);