entry point for patching 3DS modules.
## Roadmap
-Right now, this can serve as an open-source replacement for the built in loader.
+Right now, this can serve as an open-source replacement for the built in loader,
+and then some.
-There is additional support for patching any executable after it's loaded but
+There is 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.
+This currently requires recompilation which makes it less than ideal.
+
+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.
## Changes I've made
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 when you know it is in text?
+text, data and ro (big) when you know it is in text?
## 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, it required manual conflict merges
-but he provided the information needed to fix it up for the most part. ;P
+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.
## Build
You need a working 3DS build environment with a fairly recent copy of devkitARM,
ctrulib, and makerom.
-Currently, there is no support for FIRM building in toolchain; whatever method
-you use to inject this is up to you, but the software must have support for
-resizing the sysmodule segment, or you must make sure the module size matches
-the original.
+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
return;
}
-/*
-static int
-loadTitleLocaleConfig(u64 progId, u8* regionId, u8* languageId)
+
+static int loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
{
- // FIXME - Rewrite this function to use a single line-based config
+ // FIXME - Rewrite this function to use a single line-based config of
+ // the grammar:
+
+ // lang := "ja" | "en" | "fr" | "de" | "it" | "es" | "zh" | "ko" | "nl" | "pt" | "ru" | "tw"
+ // region := "JP" | "US" | "EU" | "AU" | "CN" | "KO" | "TW"
+ // title := (0123456789abcdef)16
+ // langcode := lang . "_" . country
+ // line := title langcode
+
+ // So this would be valid as an example file:
+ // 0040000000012300 en_US
+ // 0040000000032100 ja_JP
// Directory seeks have severe issues on FAT and
// dumping configs based on 3dsdb (more than 1000) causes things
// to kinda choke. FAT is not meant for large numbers of files per
- // directory due to linear seeks rather than tree or hash-based indexes
-
- char path[] = "/corbenik/etc/locale.conf"; // The locale config file.
+ // directory due to linear seeks rather than tree or hash-based indexes.
- char progid_str[16];
+ // This really does need a rewrite.
- // Hexdump.
- for (int i = 0; i < 16; i += 2) {
- progid_str[i] = ("0123456789ABCDEF")[(((u8*)&progId)[0] >> 4) & 0xf];
- progid_str[i + 1] = ("0123456789ABCDEF")[((u8*)&progId)[0] & 0xf];
- }
+ char path[] = "/corbenik/etc/locale/0000000000000000";
+ u32 i = 36;
+ while(progId) {
+ static const char hexDigits[] = "0123456789ABCDEF";
+ path[i--] = hexDigits[(u32)(progId & 0xF)];
+ progId >>= 4;
+ }
- static const char* regions[] = { "JPN", "USA", "EUR", "AUS",
+ static const char* regions[] = { "JPN", "USA", "EUR", "AUS",
"CHN", "KOR", "TWN" };
- static const char* languages[] = { "JA", "EN", "FR", "DE", "IT", "ES",
+ static const char* languages[] = { "JA", "EN", "FR", "DE", "IT", "ES",
"ZH", "KO", "NL", "PT", "RU", "TW" };
-
- IFile file;
- Result eof = fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ);
- if (R_SUCCEEDED(eof)) {
- char c;
- char buf_prog[16];
- char country_tmp[16];
- char lang_tmp[16];
- u64 total;
-
- // TODO - Open and seek file properly
-
- int i = 0;
- int state = 0; // State. 0 = get_titleid, 1 = get_region, 2 =
- // get_language, 3 = process entry
- while (1) {
- eof = IFile_Read(&file, &total, &c, 1); // Read character.
-
- if (c == ' ' || c == '\n' || c == '\r' ||
- c == '\t') // Skip space characters.
- continue;
-
- if (c == '#') { // Comment! Skip until a \n or \r.
- while (c != '\n' && c != '\r') {
- eof = IFile_Read(&file, &total, &c, 1); // Read character.
- }
- continue; // Resume loop.
- }
-
- switch (state) {
- case 0:
- // Read titleID.
- for (i = 0; i < 16; i++) {
- buf_prog[i] = c;
- if (i != 15)
- eof = IFile_Read(&file, &total, &c,
- 1); // Read character.
- }
- state = 1;
- break;
- case 1:
- // Read country name. Must be <16 chars, past that is
- // ignored.
- i = 0;
- while (c != ' ' && c != '\n' && c != '\r' &&
- c != '\t') { // While we're not reading a space...
- if (i < 15) {
- country_tmp[i] = c;
- i++;
- }
- eof = IFile_Read(&file, &total, &c,
- 1); // Read character unless last one.
- }
- state = 2;
- break;
- case 2:
- // Read language name. See country name, code is basically
- // identical.
- i = 0;
- while (c != ' ' && c != '\n' && c != '\r' &&
- c != '\t') { // While we're not reading a space...
- if (i < 15) { // Only read in if <16.
- lang_tmp[i] = c;
- i++;
- }
- eof = IFile_Read(&file, &total, &c,
- 1); // Read character unless last one.
- }
- state = 3;
- break;
- case 3:
- // Process entry, return and apply if matched.
- if (!memcmp(progid_str, buf_prog, 16)) {
- // TitleID matched. Apply language emulation; but first,
- // process language and country.
- for (i = 0; i < 7; i++) {
- if (!memcmp(country_tmp, regions[i], 3)) {
- *regionId = (u8)i;
- }
- }
-
- for (u32 i = 0; i < 12; i++) {
- if (!memcmp(lang_tmp, languages[i], 2)) {
- *languageId = (u8)i;
- }
- }
- state = 4; // Processed. Go on.
- break;
- }
- state = 0; // Nope. Move onto the next one.
- break;
- }
-
- if (!R_SUCCEEDED(eof) || total == 0 || state == 4)
- break;
- }
- }
-
- IFile_Close(&file); // Read to memory.
- return eof;
+ Handle file;
+ Result ret = fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ);
+ if(R_SUCCEEDED(ret)) {
+ char buf[6];
+ u32 total;
+ ret = FSFILE_Read(file, &total, 0, buf, 6);
+ FSFILE_Close(file);
+
+ if(!R_SUCCEEDED(ret) || total < 6)
+ return -1;
+
+ for(u32 i = 0; i < 7; i++) {
+ if(memcmp(buf, regions[i], 3) == 0) {
+ *regionId = (u8)i;
+ break;
+ }
+ }
+
+ for(u32 i = 0; i < 12; i++) {
+ if(memcmp(buf + 4, languages[i], 2) == 0) {
+ *languageId = (u8)i;
+ break;
+ }
+ }
+ }
+ return ret;
}
static u8*
}
}
}
-*/
+
static void
adjust_cpu_settings(u64 progId, u8* code, u32 size)
{
}
}
-/*
+
void
language_emu(u64 progId, u8* code, u32 size)
{
}
}
}
-*/
+
void
overlay_patch(u64 progId, u8* code, u32 size)
{
}
default: // Anything else.
{
- // language_emu(progId, text, orig_size);
+ language_emu(progId, text, orig_size);
break;
}
}