Added improved language emulation from @TuxSH (improves compatibility), fixed language emulation only working for the first launched game/app and not being applied on demos

This commit is contained in:
Aurora 2016-04-15 03:34:08 +02:00
commit 7eebfd4f6a
4 changed files with 90 additions and 77 deletions

View File

@ -35,7 +35,6 @@ objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
bundled = $(dir_build)/patches.h $(dir_build)/loader.h $(dir_build)/screeninit.h bundled = $(dir_build)/patches.h $(dir_build)/loader.h $(dir_build)/screeninit.h
.PHONY: all .PHONY: all
all: launcher a9lh ninjhax all: launcher a9lh ninjhax

View File

@ -125,9 +125,10 @@ static int loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
char path[] = "/aurei/locales/0000000000000000.txt"; char path[] = "/aurei/locales/0000000000000000.txt";
u32 i = 30;
while(progId > 0) while(progId > 0)
{ {
static u32 i = 30;
static const char hexDigits[] = "0123456789ABCDEF"; static const char hexDigits[] = "0123456789ABCDEF";
path[i--] = hexDigits[(u32)(progId & 0xF)]; path[i--] = hexDigits[(u32)(progId & 0xF)];
progId >>= 4; progId >>= 4;
@ -171,91 +172,97 @@ static int loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
return ret; return ret;
} }
static u8 *getCfgOffsets(u8* code, u32 size, u32 *CFGUHandleOffset) static u8 *getCfgOffsets(u8 *code, u32 size, u32 *CFGUHandleOffset)
{ {
static const u8 CFGU_GetConfigInfoBlk2_endPattern[] = { /* HANS:
0x10, 0x80, 0xBD, 0xE8, 0x82, 0x00, 0x01, 0x00 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) */
u8 *CFGU_GetConfigInfoBlk2_endPos = code; u32 n = 0,
possible[24];
while((CFGU_GetConfigInfoBlk2_endPos + sizeof(CFGU_GetConfigInfoBlk2_endPattern)) < (code + size) for(u8 *pos = code + 4; n < 24 && pos < code + size - 4; pos += 4)
&& (*CFGUHandleOffset > 0x10000000UL || ((u32)CFGU_GetConfigInfoBlk2_endPos % 4) != 0))
{ {
//There might be multiple implementations of GetConfigInfoBlk2 but let's search for the one we want if(*(u32 *)pos == 0xD8A103F9)
CFGU_GetConfigInfoBlk2_endPos += sizeof(CFGU_GetConfigInfoBlk2_endPattern); {
CFGU_GetConfigInfoBlk2_endPos = memsearch(CFGU_GetConfigInfoBlk2_endPos, CFGU_GetConfigInfoBlk2_endPattern, for(u32 *l = (u32 *)pos - 4; n < 24 && l < (u32 *)pos + 4; l++)
size - (u32)(CFGU_GetConfigInfoBlk2_endPos - code), sizeof(CFGU_GetConfigInfoBlk2_endPattern)); if(*l <= 0x10000000) possible[n++] = *l;
}
if(CFGU_GetConfigInfoBlk2_endPos == NULL) break;
*CFGUHandleOffset = *(u32 *)(CFGU_GetConfigInfoBlk2_endPos + 8);
} }
return CFGU_GetConfigInfoBlk2_endPos; 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) static void patchCfgGetLanguage(u8 *code, u32 size, u8 languageId, u8 *CFGU_GetConfigInfoBlk2_endPos)
{ {
u8 *CFGU_GetConfigInfoBlk2_startPos = CFGU_GetConfigInfoBlk2_endPos; //Let's find STMFD SP (there might be a NOP before, but nevermind) u8 *CFGU_GetConfigInfoBlk2_startPos; //Let's find STMFD SP (there might be a NOP before, but nevermind)
while(*(u16 *)CFGU_GetConfigInfoBlk2_startPos != 0xE92D) CFGU_GetConfigInfoBlk2_startPos -= 2;
CFGU_GetConfigInfoBlk2_startPos -= 2;
u8 *languageBlkIdPos = code; for(CFGU_GetConfigInfoBlk2_startPos = CFGU_GetConfigInfoBlk2_endPos - 4;
u32 patched = 0; CFGU_GetConfigInfoBlk2_startPos >= code && *((u16 *)CFGU_GetConfigInfoBlk2_startPos + 1) != 0xE92D;
CFGU_GetConfigInfoBlk2_startPos -= 2);
while((languageBlkIdPos + 4) < (code + size) && !patched) for(u8 *languageBlkIdPos = code; languageBlkIdPos < code + size; languageBlkIdPos += 4)
{ {
static const u32 languageBlkId = 0xA0002; if(*(u32 *)languageBlkIdPos == 0xA0002)
languageBlkIdPos += 4;
languageBlkIdPos = memsearch(languageBlkIdPos, &languageBlkId, size - (u32)(languageBlkIdPos - code), 4);
if(languageBlkIdPos == NULL) break;
if(((u32)languageBlkIdPos % 4) != 0) continue;
for(u8 *instr = languageBlkIdPos - 8; instr >= languageBlkIdPos - 0x1008; instr -= 4) //Should be enough
{ {
if(*(instr + 3) != 0xEB) continue; //We're looking for BL for(u8 *instr = languageBlkIdPos - 8; instr >= languageBlkIdPos - 0x1008 && instr >= code + 4; instr -= 4) //Should be enough
u32 low24 = (*(u32 *)instr & 0x00FFFFFF) << 2;
s32 offset = (((low24 >> 25) != 0) ? -low24 : low24) + 8; //Branch offset + 8 for prefetch
if((instr + offset) >= (CFGU_GetConfigInfoBlk2_startPos - 4) && (instr + offset) <= CFGU_GetConfigInfoBlk2_endPos)
{ {
*(u32 *)(instr - 4) = 0xE3A00000 | languageId; // mov r0, sp => mov r0, =languageID if(*(instr + 3) == 0xEB) //We're looking for BL
*(u32 *)instr = 0xE5CD0000; // bl CFGU_GetConfigInfoBlk2 => strb r0, [sp] {
*(u32 *)(instr + 4) = 0xE3A00000; // mov r1, pc => mov r0, 0 (result code) u32 low24 = (*(u32 *)instr & 0x00FFFFFF) << 2;
u32 signMask = (u32)(-(low24 >> 25)) & 0xFC000000; //Sign extension
s32 offset = (s32)(low24 | signMask) + 8; //Branch offset + 8 for prefetch
//We're done if(instr + offset >= CFGU_GetConfigInfoBlk2_startPos - 4 && instr + offset <= CFGU_GetConfigInfoBlk2_endPos)
patched = 1; {
break; *((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) static void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHandleOffset)
{ {
static const u8 cfgSecureInfoGetRegionCmdPattern[] = { for(u8 *cmdPos = code; cmdPos < code + size - 28; cmdPos += 4)
0x70, 0x4F, 0x1D, 0xEE, 0x02, 0x08, 0xA0, 0xE3, 0x80, 0x00, 0xA4, 0xE5
};
u8 *cmdPos = code;
while((cmdPos + sizeof(cfgSecureInfoGetRegionCmdPattern)) < (code + size))
{ {
cmdPos += sizeof(cfgSecureInfoGetRegionCmdPattern); static const u32 cfgSecureInfoGetRegionCmdPattern[] = {0xEE1D4F70, 0xE3A00802, 0xE5A40080};
cmdPos = memsearch(cmdPos, cfgSecureInfoGetRegionCmdPattern, size - (u32)(cmdPos - code), sizeof(cfgSecureInfoGetRegionCmdPattern));
if(cmdPos == NULL) break; u32 *cmp = (u32 *)cmdPos;
if(*(u16 *)(cmdPos + 12 + 2) != 0xE59F) continue; // ldr r0, [pc, X]
if(*(u32 *)(cmdPos + 12 + 8 + *(u16 *)(cmdPos + 12)) == *CFGUHandleOffset) 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 + 16) = 0xE3A00000 | regionID; // mov r0, =regionID *((u32 *)cmdPos + 4) = 0xE3A00000 | regionId; // mov r0, =regionId
*(u32 *)(cmdPos + 20) = 0xE5C40008; // strb r0, [r4, 8] *((u32 *)cmdPos + 5) = 0xE5C40008; // strb r0, [r4, 8]
*(u32 *)(cmdPos + 24) = 0xE3A00000; // mov r0, 0 (result code) *((u32 *)cmdPos + 6) = 0xE3B00000; // movs r0, 0 (result code) ('s' not needed but nvm)
*(u32 *)(cmdPos + 28) = 0xE5840004; // str r0, [r4, 4] *((u32 *)cmdPos + 7) = 0xE5840004; // str r0, [r4, 4]
//The remaining, not patched, function code will do the rest for us //The remaining, not patched, function code will do the rest for us
break; break;
@ -443,27 +450,33 @@ void patchCode(u64 progId, u8 *code, u32 size)
} }
default: default:
if((progId & 0xFFFFFFFF00000000LL) == 0x0004000000000000LL && R_SUCCEEDED(loadConfig()) && CONFIG(4)) if(R_SUCCEEDED(loadConfig()) && CONFIG(4))
{ {
//Language emulation u32 tidHigh = (progId & 0xFFFFFFF000000000LL) >> 0x24;
u8 regionId = 0xFF,
languageId = 0xFF;
int ret = loadTitleLocaleConfig(progId, &regionId, &languageId); if(tidHigh == 0x0004000)
if(R_SUCCEEDED(ret))
{ {
u32 CFGUHandleOffset = 0xFFFFFFFF; //Language emulation
u8 *CFGU_GetConfigInfoBlk2_endPos = getCfgOffsets(code, size, &CFGUHandleOffset); u8 regionId = 0xFF,
languageId = 0xFF;
if(CFGU_GetConfigInfoBlk2_endPos != NULL) int ret = loadTitleLocaleConfig(progId, &regionId, &languageId);
if(R_SUCCEEDED(ret))
{ {
if(languageId != 0xFF) patchCfgGetLanguage(code, size, languageId, CFGU_GetConfigInfoBlk2_endPos); u32 CFGUHandleOffset;
if(regionId != 0xFF) patchCfgGetRegion(code, size, regionId, &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; break;
}
} }
} }

View File

@ -72,11 +72,11 @@ void configureCFW(const char *configPath)
} }
endPos += SPACING_Y / 2; endPos += SPACING_Y / 2;
u32 color = COLOR_RED;
//Display all the normal options in white except for the first one //Display all the normal options in white except for the first one
for(u32 i = 0; i < singleOptionsAmount; i++) for(u32 i = 0; i < singleOptionsAmount; i++)
{ {
static u32 color = COLOR_RED;
singleOptions[i].posY = endPos + SPACING_Y; singleOptions[i].posY = endPos + SPACING_Y;
endPos = drawString(singleOptionsText[i], 10, singleOptions[i].posY, color); endPos = drawString(singleOptionsText[i], 10, singleOptions[i].posY, color);
if(singleOptions[i].enabled) drawCharacter(selected, 10 + SPACING_X, singleOptions[i].posY, color); if(singleOptions[i].enabled) drawCharacter(selected, 10 + SPACING_X, singleOptions[i].posY, color);

View File

@ -114,11 +114,12 @@ void firmRead(void *dest, const char *firmFolder)
//Complete the string with the .app name //Complete the string with the .app name
memcpy(&path[34], "/00000000.app", 14); memcpy(&path[34], "/00000000.app", 14);
u32 i = 42;
//Convert back the .app name from integer to array //Convert back the .app name from integer to array
while(id > 0) while(id > 0)
{ {
//Last digit of the .app //Last digit of the .app
static u32 i = 42;
static const char hexDigits[] = "0123456789ABCDEF"; static const char hexDigits[] = "0123456789ABCDEF";
path[i--] = hexDigits[id & 0xF]; path[i--] = hexDigits[id & 0xF];
id >>= 4; id >>= 4;