From ff4203ee27b483d481a253f177df284f5876dd20 Mon Sep 17 00:00:00 2001 From: chaoskagami Date: Mon, 23 May 2016 19:44:20 -0400 Subject: [PATCH] Experimental single-file locale emu with comment support. No clue whether it will work yet --- Makefile | 9 ++- external/loader/source/config.h | 68 +---------------- external/loader/source/patcher.c | 127 +++++++++++++++++++++---------- host/generate_langemu_conf.sh | 38 +++++++++ source/config.c | 7 +- source/config.h | 1 + source/patch_format.h | 2 +- 7 files changed, 135 insertions(+), 117 deletions(-) create mode 100755 host/generate_langemu_conf.sh diff --git a/Makefile b/Makefile index 061813b..ecb94ee 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ objects_cfw = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ $(call rwildcard, $(dir_source), *.s *.c))) .PHONY: all -all: a9lh modules external +all: a9lh modules external host/langemu.conf .PHONY: modules modules: @@ -41,10 +41,17 @@ external: .PHONY: a9lh a9lh: $(dir_out)/arm9loaderhax.bin +host/langemu.conf: + echo "Generating langemu.conf - may take a bit" + cd host && ./generate_langemu_conf.sh + mkdir -p $(dir_out)/corbenik/etc + cp host/langemu.conf $(dir_out)/corbenik/etc/langemu.conf + .PHONY: clean clean: make -C modules clean make -C external clean + rm host/langemu.conf rm -rf $(dir_out) $(dir_build) .PHONY: $(dir_out)/arm9loaderhax.bin diff --git a/external/loader/source/config.h b/external/loader/source/config.h index 0602091..9e298f3 100644 --- a/external/loader/source/config.h +++ b/external/loader/source/config.h @@ -8,6 +8,7 @@ __attribute__((unused)) static unsigned int config_version = 1; // Structure of config file struct config_file { char magic[4]; // "OVAN" for shits and giggles again. + uint32_t config_ver; // Config file version. uint8_t options[256]; // Options in the menu - deliberately large to avoid config version bumps. @@ -15,75 +16,8 @@ struct config_file { uint64_t patch_ids[256]; // What patches are enabled by UUID. 256 is an arbitrary limit - contact me if you hit it. }__attribute__((packed)); -/* -extern struct config_file config; - -enum type { - boolean_val, // Toggle - ranged_val // N1 - N2, left and right to pick. - mask_val // Bitmask allowed values. -}; - -struct range_str { - int a, b; -}; - -struct option { - int config_index; - char name_text[64]; - enum type allowed; - uint32_t a, b; -}__attribute__((packed)); - - -static struct options[] = { - { 0, "Signature Patch", boolean_val, 0 }, - { 1, "FIRM Protection", boolean_val, 0 }, - { 2, "SysModule Replacement", boolean_val, 0 }, - { 3, "Service Replacement", boolean_val, 0 }, - { 4, "ARM9 Thread", boolean_val, 0 }, - - { 5, "Autoboot", boolean_val, 0 }, - { 6, "Silence w/ Autoboot", boolean_val, 0 }, - { 7, "Step through with button", boolean_val, 0 }, - - { 8, "Don't draw background color", boolean_val, 0 }, - { 9, "Preserve framebuffer data", boolean_val, 0 }, - - { 10, "Hide Help from menu", boolean_val, 0 }, - - { 11, "Loader: CPU L2 enable", boolean_val, 0 }, - { 12, "Loader: CPU 800Mhz mode", boolean_val, 0 }, - { 13, "Loader: Language Emulation", boolean_val, 0 }, - - { 14, "No dependency tracking", boolean_val, 0 }, - { 15, "Allow unsafe options", boolean_val, 0 }, -} -*/ -#define OPTION_SIGPATCH 0 // Use builtin signature patch. -#define OPTION_FIRMPROT 1 // Protect firmware from writes. -#define OPTION_LOADER 2 // Use builtin loader module replacer. -#define OPTION_SERVICES 3 // Inject services (including backdoor for 11) -#define OPTION_ARM9THREAD 4 // Use builtin ARM9 thread injector. - -#define OPTION_AUTOBOOT 5 // Skip menu unless L is held. -#define OPTION_SILENCE 6 // Don't print debug information. -#define OPTION_TRACE 7 // Pause for A key on each step. - -#define OPTION_TRANSP_BG 8 // Background color is not drawn under text. -#define OPTION_NO_CLEAR_BG 9 // Framebuffer is preserved from whatever ran before us. -#define OPTION_READ_ME 10 // Remove Help/Readme from menu. - #define OPTION_LOADER_CPU_L2 11 // Enable L2 cache. #define OPTION_LOADER_CPU_800MHZ 12 // Enable 800Mhz mode. #define OPTION_LOADER_LANGEMU 13 // Enable 800Mhz mode. -#define IGNORE_PATCH_DEPS 14 // Ignore patch UUID dependencies. Not recommended. -#define IGNORE_BROKEN_SHIT 15 // Allow enabling patches which are marked as 'incompatible'. Chances are there's a reason. - -//#define HEADER_COLOR 12 // Color of header text. -//#define BG_COLOR 13 // Color of background. -//#define TEXT_COLOR 14 // Color of most text. -//#define ARROW_COLOR 15 // Color of Arrow. - #endif diff --git a/external/loader/source/patcher.c b/external/loader/source/patcher.c index f76cf25..26c1545 100644 --- a/external/loader/source/patcher.c +++ b/external/loader/source/patcher.c @@ -151,64 +151,107 @@ static int loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId) char path[] = "/corbenik/etc/locale.conf"; // The locale config file. - char progIdStr[17]; // Sizeof titleid as string + null terminator - progIdStr[sizeof(progIdStr)-1] = 0; // Set null term - - // Hexdump progId - for(int i=0; i < 8; i++) { - static const char hexDigits[] = "0123456789ABCDEF"; - progIdStr[i*2] = hexDigits[(((uint8_t*)&progId)[i] >> 4) & 0xf]; - progIdStr[i*2+1] = hexDigits[ ((uint8_t*)&progId)[i] & 0xf]; + char progid_str[16]; + + // 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 ]; } + static const char *regions[] = {"JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"}; + static const char *languages[] = {"JA", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"}; + IFile file; - Result ret = fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ); - if(R_SUCCEEDED(ret)) + Result eof = fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ); + if(R_SUCCEEDED(eof)) { - char buf[6]; + char c; + char buf_prog[16]; + char country_tmp[16]; + char lang_tmp[16]; u64 total; // TODO - Open and seek file properly - ret = IFile_Read(&file, &total, buf, 6); - IFile_Close(&file); - - if(!R_SUCCEEDED(ret) || total < 6) return -1; + 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. - // TODO - Split by nl/spaces strtok style. This is not acceptable code. - // Entries should be of the format: - // - // No attempt will be made to fix dumbass use of CRLF or CR-only, but newline - // characters should be treated as spaces. - - for(u32 i = 0; i < 7; ++i) - { - // TODO - Permit alternative names. They're using fixed strings for ease of use; - // we need to permit names like 'Japan', 'Europe', etc - // Maybe read locale data from the FS? http://cldr.unicode.org/ - static const char *regions[] = {"JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"}; + if (c == ' ' || c == '\n' || c == '\r' || c == '\t') // Skip space characters. + continue; - if(memcmp(buf, regions[i], 3) == 0) - { - *regionId = (u8)i; - break; + if (c == '#') { // Comment! Skip until a \n or \r. + while(c != '\n' && c != '\r') { + eof = IFile_Read(&file, &total, &c, 1); // Read character. } - } - - for(u32 i = 0; i < 12; ++i) - { - // TODO - Same as above. - static const char *languages[] = {"JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"}; + 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; + } + } - if(memcmp(buf + 4, languages[i], 2) == 0) - { - *languageId = (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; } } - return ret; + IFile_Close(&file); // Read to memory. + return eof; } static u8 *getCfgOffsets(u8 *code, u32 size, u32 *CFGUHandleOffset) diff --git a/host/generate_langemu_conf.sh b/host/generate_langemu_conf.sh new file mode 100755 index 0000000..5354778 --- /dev/null +++ b/host/generate_langemu_conf.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# Downloads the XML from 3dsdb, parses it, and using this information generates +# a langemu config for single-language single-region games, which can have langemu +# without any ill consequences. + +# Fetch XML. +wget "http://3dsdb.com/xml.php" -O 3ds.tmp + +# Extract all needed fields: titleID, region, language +grep '' 3ds.tmp | sed -e 's|[[:space:]]*||g' -e 's|||g' > titleid.tmp +grep '' 3ds.tmp | sed -e 's|[[:space:]]*||g' -e 's|||g' > region.tmp +grep '' 3ds.tmp | sed -e 's|[[:space:]]*||g' -e 's|||g' > languages.tmp + +ENTS=0 + +# Open all three files and read into full file, checking validity as we go +while true; do + read -r titleid <&3 || break + read -r region <&4 || break + read -r languages <&5 || break + + if [ "${#titleid}" == "16" ]; then + # Length of TID is correct. + echo "$languages" | grep ',' 2>&1 >/dev/null + R=$? + if [ ! $R = 0 ]; then + # Only one language found, since no comma. + # Output an entry. + echo "$titleid $region $languages" | tr [:lower:] [:upper:] | sed -e 's|GER|EUR|g' -e 's|ITA|EUR|g' -e 's|FRA|EUR|g' -e 's|UKV|EUR|g' -e 's|NLD|EUR|g' -e 's|WLD|JPN|g' >> langemu.tmp + ENTS=$((ENTS + 1)) + fi + fi +done 3 langemu.conf +cat langemu.tmp | sort | uniq >> langemu.conf +rm -f *.tmp diff --git a/source/config.c b/source/config.c index b8687bf..497a62e 100644 --- a/source/config.c +++ b/source/config.c @@ -6,12 +6,7 @@ struct config_file config; void regenerate_config() { f_mkdir(PATH_CFW); - f_mkdir(PATH_CFW "/lib"); - f_mkdir(PATH_FIRMWARES); - f_mkdir(PATH_PATCHES); - f_mkdir(PATH_TEMP); - f_mkdir(PATH_KEYS); - f_mkdir(PATH_EXEFS); + f_mkdir(PATH_CONFIG); fprintf(BOTTOM_SCREEN, "Created directory structure.\n"); diff --git a/source/config.h b/source/config.h index c87a314..e972179 100644 --- a/source/config.h +++ b/source/config.h @@ -8,6 +8,7 @@ _UNUSED static unsigned int config_version = 1; // Structure of config file struct config_file { char magic[4]; // "OVAN" for shits and giggles again. + uint32_t config_ver; // Config file version. uint8_t options[256]; // Options in the menu - deliberately large to avoid config version bumps. diff --git a/source/patch_format.h b/source/patch_format.h index 27d13cc..2f624dd 100644 --- a/source/patch_format.h +++ b/source/patch_format.h @@ -29,7 +29,7 @@ #define PATH_CONFIG_DIR PATH_CFW "/etc" // Config file directory. #define PATH_CONFIG PATH_CONFIG_DIR "/main.conf" // Config file. -#define PATH_LOCEMU PATH_CONFIG_DIR "/locale.conf" // Locale emulation config +#define PATH_LOCEMU PATH_CONFIG_DIR "/langemu.conf" // Locale emulation config #define PATH_CPU_CFG PATH_CONFIG_DIR "/cpu.conf" // CPU settings config #define PATH_PATCHES PATH_CFW "/bin" // Patch binary folder. -- 2.39.5