Aside from a few ones that involve complicated steps, of course.
It's STILL smaller than Nintendo's loader, incidentally!
(A few things need tweaking - namely, it is very slow to boot.
I expect I need some kind of patch cache generated prior to boot.
I also need to turn off the altmenu patch.)
// 'Tis not ready for the world at large yet.
// I don't want to delete it since I'm working on it though,
// so it's temporarliy #if'd 0.
-#if 0
-
#define LOADER 1
#include <3ds.h>
#include "config.h"
#include "../../../source/patch_format.h"
-#include "patch/patch.h"
+// Because I want to avoid malloc.
+// Patches must be smaller than this (including header!)
+#define MAX_PATCHSIZE 512
// Yes, we're including a C file. Problem?
#include "../../../source/interp.c"
+#if 0
+
#endif
--- /dev/null
+#ifndef __INTERP_H
+#define __INTERP_H
+
+int execb(char* filename, uint64_t tid, uint8_t* search_mem, uint32_t search_len);
+
+#endif
#include "config.h"
#include "../../../source/patch_format.h"
-#include "patch/patch.h"
-
Handle log_file_hdl;
int logger_is_initd = 0;
return;
// Write data.
- FSFILE_Write(log_file_hdl, &wrote, size, str, len, 0);
+ FSFILE_Write(log_file_hdl, &wrote, size, str, len, FS_WRITE_FLUSH);
}
void logu64(u64 progId) {
#include "config.h"
#include "../../../source/patch_format.h"
-#include "patch/patch.h"
-
int
memcmp(const void* buf1, const void* buf2, u32 size)
{
+++ /dev/null
-#include "patch.h"
-
-/*
- find 4, 0x0C, 0x18, 0xE1, 0xD8
- set 4, 0x0B, 0x18, 0x21, 0xC8
-
- find 4, 0x0C, 0x18, 0xE1, 0xD8
- set 4, 0x0B, 0x18, 0x21, 0xC8
-*/
-
-void
-disable_cart_updates(u64 progId, u8* code, u32 size)
-{
- 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);
-
- logstr(" disable_cart_updates\n");
-}
+++ /dev/null
-#include "patch.h"
-
-/*
- find 4, 0x30, 0xB5, 0xF1, 0xB0
- set 6, 0x00, 0x20, 0x08, 0x60, 0x70, 0x47
- */
-
-void
-disable_eshop_updates(u64 progId, u8* code, u32 size)
-{
- static const u8 skipEshopUpdateCheckPattern[] = { 0x30, 0xB5, 0xF1, 0xB0 };
- static const u8 skipEshopUpdateCheckPatch[] = { 0x00, 0x20, 0x08,
- 0x60, 0x70, 0x47 };
-
- // Skip update checks to access the EShop
- patchMemory(code, size, skipEshopUpdateCheckPattern,
- sizeof(skipEshopUpdateCheckPattern), 0,
- skipEshopUpdateCheckPatch, sizeof(skipEshopUpdateCheckPatch),
- 1);
-
- logstr(" disable_eshop_updates\n");
-}
+++ /dev/null
-#include "patch.h"
-
-/*
- find 4, 0x25, 0x79, 0x0B, 0x99
- set 2, 0xE3, 0xA0
- */
-
-void
-disable_nim_updates(u64 progId, u8* code, u32 size)
-{
- static const u8 blockAutoUpdatesPattern[] = { 0x25, 0x79, 0x0B, 0x99 };
- static const u8 blockAutoUpdatesPatch[] = { 0xE3, 0xA0 };
-
- // Block silent auto-updates
- patchMemory(code, size, blockAutoUpdatesPattern,
- sizeof(blockAutoUpdatesPattern), 0, blockAutoUpdatesPatch,
- sizeof(blockAutoUpdatesPatch), 1);
-
- logstr(" disable_nim_updates\n");
-}
+++ /dev/null
-#include "patch.h"
-
-/*
- find 8, 0x14, 0x00, 0xD0, 0xE5, 0xDB, 0x9A, 0x9F, 0xED
- set 4, 0x00, 0x00, 0xA0, 0xE3
-
- find 8, 0x14, 0x00, 0xD0, 0xE5, 0x01, 0x00, 0x10, 0xE3
- set 4, 0x00, 0x00, 0xA0, 0xE3
- find 8, 0x14, 0x00, 0xD0, 0xE5, 0x01, 0x00, 0x10, 0xE3
- set 4, 0x00, 0x00, 0xA0, 0xE3
- find 8, 0x14, 0x00, 0xD0, 0xE5, 0x01, 0x00, 0x10, 0xE3
- set 4, 0x00, 0x00, 0xA0, 0xE3
- */
-
-void errdisp_devpatch(u64 progId, u8* code, u32 size) {
- static const u8 unitinfoCheckPattern1[] = {0x14, 0x00, 0xD0, 0xE5, 0xDB, 0x9A, 0x9F, 0xED};
- static const u8 unitinfoCheckPattern2[] = {0x14, 0x00, 0xD0, 0xE5, 0x01, 0x00, 0x10, 0xE3} ;
- static const u8 unitinfoCheckPatch[] = {0x00, 0x00, 0xA0, 0xE3} ;
-
- patchMemory(code, size,
- unitinfoCheckPattern1,
- sizeof(unitinfoCheckPattern1), 0,
- unitinfoCheckPatch,
- sizeof(unitinfoCheckPatch), 1
- );
-
- patchMemory(code, size,
- unitinfoCheckPattern2,
- sizeof(unitinfoCheckPattern2), 0,
- unitinfoCheckPatch,
- sizeof(unitinfoCheckPatch), 3
- );
-}
+++ /dev/null
-#include "patch.h"
-
-/*
- find 8, 0xE0, 0x1E, 0xFF, 0x2F, 0xE1, 0x01, 0x01, 0x01
- fwd 9
- set 1, 0x06
- */
-
-void
-fake_friends_version(u64 progId, u8* code, u32 size)
-{
- static const u8 fpdVerPattern[] = { 0xE0, 0x1E, 0xFF, 0x2F,
- 0xE1, 0x01, 0x01, 0x01 };
- static const u8 fpdVerPatch = 0x06; // Latest version.
-
- // 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");
-}
+++ /dev/null
-#include "patch.h"
-
-/*
- find 8, u"Ver."
- set 8, u".hax"
- */
-
-void
-settings_string(u64 progId, u8* code, u32 size)
-{
- static const u16 verPattern[] = u"Ver.";
- static const u16 verPatch[] = u".hax";
-
- // Patch Ver. string
- patchMemory(code, size, verPattern, sizeof(verPattern) - sizeof(u16), 0,
- verPatch, sizeof(verPatch) - sizeof(u16), 1);
-
- logstr(" settings_string\n");
-}
+++ /dev/null
-#ifndef __PATCH_PATCH_H
-#define __PATCH_PATCH_H
-
-#include <3ds.h>
-#include "../patcher.h"
-#include "../memory.h"
-#include "../logger.h"
-
-#ifndef PATH_MAX
-#define PATH_MAX 255
-#define _MAX_LFN 255
-#endif
-#include "../config.h"
-#include "../../../../source/patch_format.h"
-
-void disable_cart_updates(u64 progId, u8* code, u32 size);
-void disable_eshop_updates(u64 progId, u8* code, u32 size);
-void disable_nim_updates(u64 progId, u8* code, u32 size);
-void fake_friends_version(u64 progId, u8* code, u32 size);
-void settings_string(u64 progId, u8* code, u32 size);
-void region_patch(u64 progId, u8* code, u32 size);
-void ro_sigpatch(u64 progId, u8* code, u32 size);
-void secureinfo_sigpatch(u64 progId, u8* code, u32 size);
-void errdisp_devpatch(u64 progId, u8* code, u32 size);
-
-#endif
+++ /dev/null
-#include "patch.h"
-
-/*
- find 8, 0x00, 0x00, 0x55, 0xE3, 0x01, 0x10, 0xA0, 0xE3
- back 16
- set 8, 0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1
- */
-
-void
-region_patch(u64 progId, u8* code, u32 size)
-{
- 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);
-
- logstr(" region_patch\n");
-}
+++ /dev/null
-#include "patch.h"
-
-/*
- find 8, 0x30, 0x40, 0x2D, 0xE9, 0x02, 0x50, 0xA0, 0xE1
- set 8, 0x00, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1
-
- find 8, 0x30, 0x40, 0x2D, 0xE9, 0x24, 0xD0, 0x4D, 0xE2
- set 8, 0x00, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1
-
- find 8, 0xF8, 0x4F, 0x2D, 0xE9, 0x01, 0x70, 0xA0, 0xE1
- set 8, 0x00, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1
- */
-
-void
-ro_sigpatch(u64 progId, u8* code, u32 size)
-{
- static const u8 sigCheckPattern[] = { 0x30, 0x40, 0x2D, 0xE9,
- 0x02, 0x50, 0xA0, 0xE1 };
- static const u8 sha256ChecksPattern1[] = { 0x30, 0x40, 0x2D, 0xE9,
- 0x24, 0xD0, 0x4D, 0xE2 };
- static const u8 sha256ChecksPattern2[] = { 0xF8, 0x4F, 0x2D, 0xE9,
- 0x01, 0x70, 0xA0, 0xE1 };
-
- // mov r0, #0; bx lr - equivalent to 'return 0;'
- static const u8 stub[] = { 0x00, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1 };
-
- // Disable CRR0 signature (RSA2048 with SHA256) check
- patchMemory(code, size, sigCheckPattern, sizeof(sigCheckPattern), 0, stub,
- sizeof(stub), 1);
-
- // Disable CRO0/CRR0 SHA256 hash checks (section hashes, and hash table)
- patchMemory(code, size, sha256ChecksPattern1, sizeof(sha256ChecksPattern1),
- 0, stub, sizeof(stub), 1);
-
- patchMemory(code, size, sha256ChecksPattern2, sizeof(sha256ChecksPattern2),
- 0, stub, sizeof(stub), 1);
-
- logstr(" ro_sigpatch\n");
-}
+++ /dev/null
-#include "patch.h"
-
-/*
- find 5, 0x06, 0x46, 0x10, 0x48, 0xFC
- set 2, 0x00, 0x26
- */
-
-void
-secureinfo_sigpatch(u64 progId, u8* code, u32 size)
-{
- 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);
-
- logstr(" secureinfo_sigpatch\n");
-}
#include "internal.h"
#include "memory.h"
#include "logger.h"
+#include "../../../source/patch_format.h"
+#include "interp.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
fileOpen(Handle* file, FS_ArchiveID id, const char* path, int flags)
FSFILE_Close(file); // Read to memory.
- if (config.magic[0] != 'O' || config.magic[1] != 'V' ||
- config.magic[2] != 'A' || config.magic[3] != 'N') {
+ if (memcmp(config.magic, "OVAN", 4)) {
// Incorrect magic.
// Failed to read.
return;
void
patch_text(u64 progId, u8* text, u32 size, u32 orig_size)
{
- 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
- {
- region_patch(progId, text, orig_size);
- break;
- }
-
- case 0x0004013000002C02LL: // NIM
- {
- disable_nim_updates(progId, text, orig_size);
- disable_eshop_updates(progId, text, orig_size);
- break;
- }
-
- case 0x0004013000003202LL: // FRIENDS
- {
- fake_friends_version(progId, text, orig_size);
- 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
- {
- settings_string(progId, text, size);
- break;
- }
- case 0x0004013000008002LL: // NS
- {
- disable_cart_updates(progId, text, orig_size);
- adjust_cpu_settings(progId, text, orig_size);
- break;
- }
-
- case 0x0004013000001702LL: // CFG
- {
- secureinfo_sigpatch(progId, text, orig_size);
- break;
- }
- case 0x0004013000003702LL: // RO
- {
- ro_sigpatch(progId, text, orig_size);
- break;
- }
- case 0x0004003000008A02LL: // ErrDisp
- {
- errdisp_devpatch(progId, text, orig_size);
- break;
- }
- default: // Anything else.
- {
- language_emu(progId, text, orig_size);
- break;
- }
- }
+ if (progId == 0x0004013000008002LL)
+ adjust_cpu_settings(progId, text, orig_size);
+
+ execb(PATH_PATCHES "/block_nim_update.vco", progId, text, orig_size);
+ execb(PATH_PATCHES "/block_eshop_update.vco", progId, text, orig_size);
+ execb(PATH_PATCHES "/block_cart_update.vco", progId, text, orig_size);
+ execb(PATH_PATCHES "/errdisp.vco", progId, text, orig_size);
+ execb(PATH_PATCHES "/friends_ver.vco", progId, text, orig_size);
+ execb(PATH_PATCHES "/mset_str.vco", progId, text, orig_size);
+ execb(PATH_PATCHES "/ns_force_menu.vco", progId, text, orig_size);
+ execb(PATH_PATCHES "/regionfree.vco", progId, text, orig_size);
+ execb(PATH_PATCHES "/secinfo_sigs.vco", progId, text, orig_size);
+ execb(PATH_PATCHES "/ro_sigs.vco", progId, text, orig_size);
+
+ language_emu(progId, text, orig_size);
}
// Gets how many bytes .text must be extended by for patches to fit.
offsets += [size]
size += len(bytes)
- print(offsets)
-
ins.seek(0)
for line in ins:
data += struct.pack('I', size)
if title:
for f in title:
- data += bytearray.fromhex(f)
+ tid = bytearray.fromhex(f) # Endianness.
+ print([tid])
+ tid.reverse()
+ print([tid])
+ data += tid
if deps:
for f in deps:
data += pad_zero_r(bytearray.fromhex(f), 8)
#define OP_TITLE 0x0A
#ifdef LOADER
- #define fprintf(a...)
- #define abort(a...)
+ #define log(a) logstr(a)
+ #define abort(a) { logstr(a) ; svcBreak(USERBREAK_ASSERT) ; }
+#else
+ #define log(a) fprintf(stderr, a)
#endif
struct mode {
switch(*code) {
case OP_NOP:
if (debug)
- fprintf(stderr, "nop\n");
+ log("nop\n");
code++;
test_was_false = 0;
break;
code += 2;
#else
if (debug)
- fprintf(stderr, "rel\n");
+ log("rel\n");
code++;
if (!test_was_false)
current_mode = &modes[*code];
break;
case OP_FIND: // Find pattern.
if (debug)
- fprintf(stderr, "find\n");
+ log("find\n");
code += 2;
if (!test_was_false) {
offset = (uint32_t)memfind(current_mode->memory+offset, current_mode->size - offset, code, *(code-1));
if ((uint8_t*)offset == NULL) {
// Error. Abort.
abort("Find opcode failed.\n");
- } else if (debug) {
- fprintf(stderr, "Match @ %x\n", offset);
}
offset = offset - (uint32_t)current_mode->memory;
} else {
break;
case OP_BACK:
if (debug)
- fprintf(stderr, "back\n");
+ log("back\n");
code++;
if (!test_was_false) {
if (offset < *code) {
break;
case OP_FWD:
if (debug)
- fprintf(stderr, "fwd\n");
+ log("fwd\n");
code++;
if (!test_was_false) {
offset += *code;
break;
case OP_SET: // Set data.
if (debug)
- fprintf(stderr, "set\n");
+ log("set\n");
code += 2;
if (!test_was_false)
memcpy(current_mode->memory+offset, code, *(code-1));
break;
case OP_TEST: // Test data.
if (debug)
- fprintf(stderr, "test\n");
+ log("test\n");
code += 2;
if(memcmp(current_mode->memory+offset, code, *(code-1))) {
test_was_false = 1;
if (debug)
- fprintf(stderr, "false\n");
+ log("false\n");
} else if (debug) {
- fprintf(stderr, "true\n");
+ log("true\n");
}
code += *(code-1);
break;
case OP_JMP: // Jump to offset.
if (debug)
- fprintf(stderr, "jmp\n");
+ log("jmp\n");
code++;
if (!test_was_false) {
- if (debug)
- fprintf(stderr, "jmp to %hu,%hu\n", code[0], code[1]);
code = bytecode + code[1] + ( code[0] * 0x100 );
} else {
code += 2;
break;
case OP_REWIND:
if (debug)
- fprintf(stderr, "rewind\n");
+ log("rewind\n");
code++;
if (!test_was_false)
offset = 0;
break;
case OP_AND:
if (debug)
- fprintf(stderr, "and\n");
+ log("and\n");
code += 2;
if (!test_was_false) {
for(i=0; i < *(code-1); i++) {
break;
case OP_TITLE:
if (debug)
- fprintf(stderr, "title\n");
+ log("title\n");
// FIXME - NYI
default:
+#ifndef LOADER
// Panic; not proper opcode.
fprintf(stderr, "Invalid opcode. State:\n"
" Relative: %u\n"
code - bytecode,
code,
*code);
+#endif
abort("Halting startup.\n");
break;
}
#else
int execb(char* filename) {
#endif
- uint8_t* patch_mem;
uint32_t patch_len;
struct system_patch* patch;
#ifdef LOADER
- // Loader can use actual allocation functions, so this isn't as much of an issue
+ log(" check ");
+ log(filename);
+ log("\n");
+
+ uint8_t patch_dat2[MAX_PATCHSIZE];
+ uint8_t *patch_dat = patch_dat2;
+
+ patch = (struct system_patch*)patch_dat;
+
+ Handle file;
+ u32 total;
+
+ // Open file.
+ if (!R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, filename, FS_OPEN_READ))) {
+ // Failed to open.
+ return 1;
+ }
+
+ u64 file_size;
+
+ if (!R_SUCCEEDED(FSFILE_GetSize(file, &file_size))) {
+ FSFILE_Close(file); // Read to memory.
+
+ return 1;
+ }
+
+ if (file_size > MAX_PATCHSIZE) {
+ log(" too large (please report)\n");
+
+ FSFILE_Close(file); // Read to memory.
+
+ return 1;
+ }
+
+ // Read file.
+ if (!R_SUCCEEDED(FSFILE_Read(file, &total, 0, patch_dat, file_size))) {
+ FSFILE_Close(file); // Read to memory.
+
+ // Failed to read.
+ return 1;
+ }
+
+ FSFILE_Close(file); // Done reading in.
// Set memory.
modes[18].memory = search_mem;
modes[18].size = search_len;
- // Load patch. We need memory, so...
- // svcControlMemory(&tmp, __loader_prog_heap, 0x0, __ctru_heap_size, MEMOP_ALLOC, MEMPERM_READ | MEMPERM_WRITE);
-
// Check magic.
+ if (memcmp(patch->magic, "AIDA", 4)) {
+ log(" magic wrong\n");
+
+ return 1; // Incorrect patch magic.
+ }
// Check TID supported.
+ if (patch->titles == 0) {
+ return 0; // Not an error - this patch isn't meant for us.
+ }
+
+ log(" checking tid\n");
+
+ uint8_t* title_buf = patch_dat + sizeof(struct system_patch);
+
+ int apply = 0;
+ for(uint32_t i=0; i < patch->titles; i++, title_buf += 8) {
+ if(!memcmp(title_buf, (uint8_t*)&tid, 8)) {
+ // Applicable patch found.
+ log(" applicable\n");
+ apply = 1;
+ break;
+ }
+ }
+
+ if (!apply) {
+ // Not meant for us. Not an error, though.
+ return 0;
+ }
+
+ // Patch is relevant to us, so we'll apply it.
+
+ log(" exec\n");
+
+ uint8_t* patch_mem = (uint8_t*)patch + sizeof(struct system_patch) + (patch->depends * 8) + (patch->titles * 8);
+ patch_len = patch->size;
#else
+ uint8_t* patch_mem;
// Read patch to scrap memory.
FILE* f = fopen(filename, "r");
patch_mem = (uint8_t*)patch + sizeof(struct system_patch) + (patch->depends * 8) + (patch->titles * 8);
patch_len = patch->size;
#endif
- return exec_bytecode(patch_mem, patch_len, 0);
+ return exec_bytecode(patch_mem, patch_len, 1);
}