2016-03-29 17:43:53 +02:00
|
|
|
#include <3ds.h>
|
|
|
|
#include "patcher.h"
|
2016-09-06 14:09:29 +02:00
|
|
|
#include "memory.h"
|
|
|
|
#include "strings.h"
|
2017-04-13 01:03:37 +02:00
|
|
|
#include "fsldr.h"
|
2016-03-29 17:43:53 +02:00
|
|
|
#include "ifile.h"
|
2016-08-29 17:51:03 +02:00
|
|
|
#include "CFWInfo.h"
|
2016-11-16 03:41:59 +01:00
|
|
|
#include "../build/bundled.h"
|
2016-03-29 17:43:53 +02:00
|
|
|
|
2016-08-28 13:49:15 +02:00
|
|
|
static CFWInfo info;
|
2016-03-29 17:43:53 +02:00
|
|
|
|
2017-05-07 23:49:49 +02:00
|
|
|
static u32 patchMemory(u8 *start, u32 size, const void *pattern, u32 patSize, s32 offset, const void *replace, u32 repSize, u32 count)
|
2016-04-02 17:58:06 +02:00
|
|
|
{
|
2016-11-06 14:45:45 +01:00
|
|
|
u32 i;
|
|
|
|
|
2017-04-29 18:12:14 +02:00
|
|
|
for(i = 0; i < count; i++)
|
2016-04-02 17:58:06 +02:00
|
|
|
{
|
2016-04-02 18:48:31 +02:00
|
|
|
u8 *found = memsearch(start, pattern, size, patSize);
|
2016-04-02 17:58:06 +02:00
|
|
|
|
2016-11-06 14:45:45 +01:00
|
|
|
if(found == NULL) break;
|
2016-03-29 17:43:53 +02:00
|
|
|
|
2016-04-02 18:48:31 +02:00
|
|
|
memcpy(found + offset, replace, repSize);
|
2016-03-29 17:43:53 +02:00
|
|
|
|
|
|
|
u32 at = (u32)(found - start);
|
|
|
|
|
2016-04-11 17:57:22 +02:00
|
|
|
if(at + patSize > size) break;
|
2016-03-29 17:43:53 +02:00
|
|
|
|
2016-04-11 17:57:22 +02:00
|
|
|
size -= at + patSize;
|
2016-04-02 18:48:31 +02:00
|
|
|
start = found + patSize;
|
2016-03-29 17:43:53 +02:00
|
|
|
}
|
2016-11-06 14:45:45 +01:00
|
|
|
|
|
|
|
return i;
|
2016-04-18 20:50:52 +02:00
|
|
|
}
|
|
|
|
|
2016-10-14 18:03:17 +02:00
|
|
|
static Result fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, int flags)
|
2016-04-02 17:58:06 +02:00
|
|
|
{
|
2016-09-11 19:17:56 +02:00
|
|
|
FS_Path filePath = {PATH_ASCII, strnlen(path, 255) + 1, path},
|
2016-05-09 03:41:00 +02:00
|
|
|
archivePath = {PATH_EMPTY, 1, (u8 *)""};
|
|
|
|
|
|
|
|
return IFile_Open(file, archiveId, archivePath, filePath, flags);
|
2016-03-29 17:43:53 +02:00
|
|
|
}
|
|
|
|
|
2017-05-20 05:09:48 +02:00
|
|
|
static bool dirCheck(FS_ArchiveID archiveId, const char *path)
|
2017-04-13 01:03:37 +02:00
|
|
|
{
|
2017-05-20 05:09:48 +02:00
|
|
|
bool ret;
|
2017-04-13 01:03:37 +02:00
|
|
|
Handle handle;
|
|
|
|
FS_Archive archive;
|
|
|
|
FS_Path dirPath = {PATH_ASCII, strnlen(path, 255) + 1, path},
|
|
|
|
archivePath = {PATH_EMPTY, 1, (u8 *)""};
|
|
|
|
|
2017-05-20 05:09:48 +02:00
|
|
|
if(R_FAILED(FSLDR_OpenArchive(&archive, archiveId, archivePath))) ret = false;
|
2017-04-13 01:03:37 +02:00
|
|
|
else
|
|
|
|
{
|
2017-05-20 05:09:48 +02:00
|
|
|
ret = R_SUCCEEDED(FSLDR_OpenDirectory(&handle, archive, dirPath));
|
|
|
|
if(ret) FSDIR_Close(handle);
|
2017-04-13 01:03:37 +02:00
|
|
|
FSLDR_CloseArchive(archive);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-04-13 02:49:19 +02:00
|
|
|
static bool openLumaFile(IFile *file, const char *path)
|
2016-10-14 18:03:17 +02:00
|
|
|
{
|
2017-06-03 03:59:18 +02:00
|
|
|
FS_ArchiveID archiveId = LOADERFLAG(ISSDMODE) ? ARCHIVE_SDMC : ARCHIVE_NAND_RW;
|
2016-10-14 18:03:17 +02:00
|
|
|
|
2017-05-20 05:09:48 +02:00
|
|
|
return R_SUCCEEDED(fileOpen(file, archiveId, path, FS_OPEN_READ));
|
2016-10-14 18:03:17 +02:00
|
|
|
}
|
|
|
|
|
2017-04-13 02:49:19 +02:00
|
|
|
static u32 checkLumaDir(const char *path)
|
2017-04-13 01:03:37 +02:00
|
|
|
{
|
2017-06-03 03:59:18 +02:00
|
|
|
FS_ArchiveID archiveId = LOADERFLAG(ISSDMODE) ? ARCHIVE_SDMC : ARCHIVE_NAND_RW;
|
2017-04-13 02:49:19 +02:00
|
|
|
|
2017-05-20 05:09:48 +02:00
|
|
|
return dirCheck(archiveId, path) ? archiveId : 0;
|
2017-04-13 01:03:37 +02:00
|
|
|
}
|
|
|
|
|
2016-11-03 18:55:40 +01:00
|
|
|
static inline void loadCFWInfo(void)
|
2016-08-20 18:45:56 +02:00
|
|
|
{
|
|
|
|
static bool infoLoaded = false;
|
2016-08-26 18:44:39 +02:00
|
|
|
|
2016-11-15 19:29:48 +01:00
|
|
|
if(infoLoaded) return;
|
2016-08-28 13:49:15 +02:00
|
|
|
|
2016-11-15 19:29:48 +01:00
|
|
|
svcGetCFWInfo(&info);
|
2016-08-26 18:44:39 +02:00
|
|
|
|
2016-11-15 19:29:48 +01:00
|
|
|
IFile file;
|
2017-04-28 12:43:56 +02:00
|
|
|
if(LOADERFLAG(ISSAFEMODE)) fileOpen(&file, ARCHIVE_SDMC, "/", FS_OPEN_READ); //Init SD card if SAFE_MODE is being booted
|
2016-11-15 19:29:48 +01:00
|
|
|
|
|
|
|
infoLoaded = true;
|
2016-08-20 18:45:56 +02:00
|
|
|
}
|
|
|
|
|
2016-11-04 22:28:33 +01:00
|
|
|
static inline bool secureInfoExists(void)
|
2016-04-02 17:58:06 +02:00
|
|
|
{
|
2016-07-02 14:44:01 +02:00
|
|
|
static bool exists = false;
|
2016-03-29 17:43:53 +02:00
|
|
|
|
2016-11-15 19:29:48 +01:00
|
|
|
if(exists) return true;
|
|
|
|
|
|
|
|
IFile file;
|
|
|
|
if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_NAND_RW, "/sys/SecureInfo_C", FS_OPEN_READ)))
|
2016-04-02 17:58:06 +02:00
|
|
|
{
|
2016-11-15 19:29:48 +01:00
|
|
|
exists = true;
|
|
|
|
IFile_Close(&file);
|
2016-03-29 17:43:53 +02:00
|
|
|
}
|
|
|
|
|
2016-07-02 14:44:01 +02:00
|
|
|
return exists;
|
2016-03-29 17:43:53 +02:00
|
|
|
}
|
|
|
|
|
2016-11-04 22:28:33 +01:00
|
|
|
static inline void loadCustomVerString(u16 *out, u32 *verStringSize, u32 currentNand)
|
2016-09-13 23:01:38 +02:00
|
|
|
{
|
2016-09-23 00:08:15 +02:00
|
|
|
static const char *paths[] = { "/luma/customversion_sys.txt",
|
|
|
|
"/luma/customversion_emu.txt",
|
|
|
|
"/luma/customversion_emu2.txt",
|
|
|
|
"/luma/customversion_emu3.txt",
|
|
|
|
"/luma/customversion_emu4.txt" };
|
2016-09-13 23:01:38 +02:00
|
|
|
|
|
|
|
IFile file;
|
2016-09-15 19:53:51 +02:00
|
|
|
|
2016-11-17 15:38:28 +01:00
|
|
|
if(!openLumaFile(&file, paths[currentNand])) return;
|
2016-09-13 23:01:38 +02:00
|
|
|
|
2016-11-15 19:29:48 +01:00
|
|
|
u64 fileSize;
|
2016-09-13 23:01:38 +02:00
|
|
|
|
2016-11-15 19:29:48 +01:00
|
|
|
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize > 62) goto exit;
|
2016-09-16 03:14:37 +02:00
|
|
|
|
2016-11-15 19:29:48 +01:00
|
|
|
u8 buf[62];
|
|
|
|
u64 total;
|
2016-09-16 03:14:37 +02:00
|
|
|
|
2016-11-15 19:29:48 +01:00
|
|
|
if(R_FAILED(IFile_Read(&file, &total, buf, fileSize))) goto exit;
|
|
|
|
|
|
|
|
static const u8 bom[] = {0xEF, 0xBB, 0xBF};
|
|
|
|
u32 finalSize = 0;
|
|
|
|
|
|
|
|
//Convert from UTF-8 to UTF-16 (Nintendo doesn't support 4-byte UTF-16, so 4-byte UTF-8 is unsupported)
|
|
|
|
for(u32 increase, fileSizeTmp = (u32)fileSize, i = (fileSizeTmp > 2 && memcmp(buf, bom, 3) == 0) ? 3 : 0;
|
|
|
|
i < fileSizeTmp && finalSize < 19; i += increase, finalSize++)
|
|
|
|
{
|
|
|
|
if((buf[i] & 0x80) == 0 && !(buf[i] == 0xA || buf[i] == 0xD))
|
|
|
|
{
|
|
|
|
increase = 1;
|
|
|
|
out[finalSize] = (u16)buf[i];
|
|
|
|
}
|
|
|
|
else if((buf[i] & 0xE0) == 0xC0 && i + 1 < fileSizeTmp && (buf[i + 1] & 0xC0) == 0x80)
|
|
|
|
{
|
|
|
|
increase = 2;
|
|
|
|
out[finalSize] = (u16)(((buf[i] & 0x1F) << 6) | (buf[i + 1] & 0x3F));
|
2016-09-13 23:01:38 +02:00
|
|
|
}
|
2016-11-15 19:29:48 +01:00
|
|
|
else if((buf[i] & 0xF0) == 0xE0 && i + 2 < fileSizeTmp && (buf[i + 1] & 0xC0) == 0x80 && (buf[i + 2] & 0xC0) == 0x80)
|
|
|
|
{
|
|
|
|
increase = 3;
|
|
|
|
out[finalSize] = (u16)(((buf[i] & 0xF) << 12) | ((buf[i + 1] & 0x3F) << 6) | (buf[i + 2] & 0x3F));
|
|
|
|
}
|
|
|
|
else break;
|
|
|
|
}
|
2016-09-13 23:01:38 +02:00
|
|
|
|
2016-11-15 19:29:48 +01:00
|
|
|
if(finalSize > 0)
|
|
|
|
{
|
|
|
|
if(finalSize > 5 && finalSize < 19) out[finalSize++] = 0;
|
|
|
|
*verStringSize = finalSize * 2;
|
2016-09-13 23:01:38 +02:00
|
|
|
}
|
2016-11-15 19:29:48 +01:00
|
|
|
|
|
|
|
exit:
|
|
|
|
IFile_Close(&file);
|
2016-09-13 23:01:38 +02:00
|
|
|
}
|
|
|
|
|
2017-06-02 05:48:29 +02:00
|
|
|
static u32 findFunctionStart(u8 *code, u32 pos)
|
|
|
|
{
|
|
|
|
while(pos >= 4)
|
|
|
|
{
|
|
|
|
pos -= 4;
|
|
|
|
if(*(u16 *)(code + pos + 2) == 0xE92D) return pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0xFFFFFFFF;
|
|
|
|
}
|
|
|
|
|
2016-11-04 22:28:33 +01:00
|
|
|
static inline u8 *getCfgOffsets(u8 *code, u32 size, u32 *CFGUHandleOffset)
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
{
|
2016-04-15 03:34:08 +02:00
|
|
|
/* HANS:
|
|
|
|
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) */
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
|
2016-04-15 03:34:08 +02:00
|
|
|
u32 n = 0,
|
|
|
|
possible[24];
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
|
2016-11-16 20:06:01 +01:00
|
|
|
for(u8 *pos = code + 16; n < 24 && pos <= code + size - 16; pos += 4)
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
{
|
2016-11-15 19:29:48 +01:00
|
|
|
if(*(u32 *)pos != 0xD8A103F9) continue;
|
|
|
|
|
|
|
|
for(u32 *l = (u32 *)pos - 4; n < 24 && l < (u32 *)pos + 4; l++)
|
|
|
|
if(*l <= 0x10000000) possible[n++] = *l;
|
2016-04-15 03:34:08 +02:00
|
|
|
}
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
|
2016-11-15 19:29:48 +01:00
|
|
|
if(!n) return NULL;
|
|
|
|
|
2016-11-16 20:06:01 +01:00
|
|
|
for(u8 *CFGU_GetConfigInfoBlk2_endPos = code; CFGU_GetConfigInfoBlk2_endPos <= code + size - 12; CFGU_GetConfigInfoBlk2_endPos += 4)
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
{
|
2016-11-15 19:29:48 +01:00
|
|
|
//There might be multiple implementations of GetConfigInfoBlk2 but let's search for the one we want
|
|
|
|
u32 *cmp = (u32 *)CFGU_GetConfigInfoBlk2_endPos;
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
|
2017-04-28 23:09:33 +02:00
|
|
|
if(cmp[0] != 0xE8BD8010 || cmp[1] != 0x00010082) continue;
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
|
2016-11-15 19:29:48 +01:00
|
|
|
for(u32 i = 0; i < n; i++)
|
2016-11-16 20:06:01 +01:00
|
|
|
if(possible[i] == cmp[2])
|
|
|
|
{
|
|
|
|
*CFGUHandleOffset = cmp[2];
|
2017-04-28 23:09:33 +02:00
|
|
|
|
2016-11-16 20:06:01 +01:00
|
|
|
return CFGU_GetConfigInfoBlk2_endPos;
|
|
|
|
}
|
2016-11-15 19:29:48 +01:00
|
|
|
|
|
|
|
CFGU_GetConfigInfoBlk2_endPos += 4;
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
}
|
|
|
|
|
2016-04-15 03:34:08 +02:00
|
|
|
return NULL;
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
}
|
|
|
|
|
2016-11-15 19:35:57 +01:00
|
|
|
static inline bool patchCfgGetLanguage(u8 *code, u32 size, u8 languageId, u8 *CFGU_GetConfigInfoBlk2_endPos)
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
{
|
2017-06-02 05:48:29 +02:00
|
|
|
u32 additive = findFunctionStart(code, (u32)(CFGU_GetConfigInfoBlk2_endPos - code));
|
|
|
|
|
|
|
|
if(additive == 0xFFFFFFFF) return false;
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
|
2017-06-02 05:48:29 +02:00
|
|
|
u8 *CFGU_GetConfigInfoBlk2_startPos = code + additive;
|
2016-11-15 19:29:48 +01:00
|
|
|
|
2016-11-16 20:06:01 +01:00
|
|
|
for(u8 *languageBlkIdPos = code; languageBlkIdPos <= code + size - 4; languageBlkIdPos += 4)
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
{
|
2016-11-15 19:29:48 +01:00
|
|
|
if(*(u32 *)languageBlkIdPos != 0xA0002) continue;
|
|
|
|
|
|
|
|
for(u8 *instr = languageBlkIdPos - 8; instr >= languageBlkIdPos - 0x1008 && instr >= code + 4; instr -= 4) //Should be enough
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
{
|
2016-11-15 19:29:48 +01:00
|
|
|
if(instr[3] != 0xEB) continue; //We're looking for BL
|
|
|
|
|
|
|
|
u8 *calledFunction = instr;
|
|
|
|
u32 i = 0;
|
|
|
|
|
|
|
|
do
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
{
|
2016-11-15 19:29:48 +01:00
|
|
|
u32 low24 = (*(u32 *)calledFunction & 0x00FFFFFF) << 2;
|
|
|
|
u32 signMask = (u32)(-(low24 >> 25)) & 0xFC000000; //Sign extension
|
|
|
|
s32 offset = (s32)(low24 | signMask) + 8; //Branch offset + 8 for prefetch
|
|
|
|
|
|
|
|
calledFunction += offset;
|
|
|
|
|
|
|
|
if(calledFunction >= CFGU_GetConfigInfoBlk2_startPos - 4 && calledFunction <= CFGU_GetConfigInfoBlk2_endPos)
|
2016-04-15 03:34:08 +02:00
|
|
|
{
|
2016-11-15 19:29:48 +01:00
|
|
|
*((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
|
2016-11-15 19:35:57 +01:00
|
|
|
return true;
|
2016-04-15 03:34:08 +02:00
|
|
|
}
|
2016-11-15 19:29:48 +01:00
|
|
|
|
|
|
|
i++;
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
}
|
2016-11-16 03:41:59 +01:00
|
|
|
while(i < 2 && calledFunction[3] == 0xEA);
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
}
|
|
|
|
}
|
2016-11-09 16:33:54 +01:00
|
|
|
|
2016-11-15 19:35:57 +01:00
|
|
|
return false;
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
}
|
|
|
|
|
2016-11-11 04:04:12 +01:00
|
|
|
static inline void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHandleOffset)
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
{
|
2016-11-16 20:06:01 +01:00
|
|
|
for(u8 *cmdPos = code; cmdPos <= code + size - 28; cmdPos += 4)
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
{
|
2016-04-15 03:34:08 +02:00
|
|
|
u32 *cmp = (u32 *)cmdPos;
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
|
2017-04-28 23:09:33 +02:00
|
|
|
if(*cmp != 0xE3A00802) continue;
|
2016-11-15 19:29:48 +01:00
|
|
|
|
|
|
|
for(u32 i = 1; i < 3; i++)
|
2017-04-28 23:09:33 +02:00
|
|
|
if((*(cmp - i) & 0xFFFF0FFF) == 0xEE1D0F70 && *((u16 *)cmdPos + 5) == 0xE59F &&
|
2016-11-15 19:29:48 +01:00
|
|
|
*(u32 *)(cmdPos + 16 + *((u16 *)cmdPos + 4)) == CFGUHandleOffset)
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
{
|
2016-11-15 19:29:48 +01:00
|
|
|
cmp[3] = 0xE3A00000 | regionId; //mov r0, =regionId
|
|
|
|
cmp[4] = 0xE5C40008; //strb r0, [r4, #8]
|
|
|
|
cmp[5] = 0xE3A00000; //mov r0, #0 (result code)
|
|
|
|
cmp[6] = 0xE5840004; //str r0, [r4, #4]
|
2016-11-09 16:33:54 +01:00
|
|
|
|
2016-11-15 19:29:48 +01:00
|
|
|
//The remaining, not patched, function code will do the rest for us
|
|
|
|
return;
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-26 18:04:02 +02:00
|
|
|
static inline bool findLayeredFsSymbols(u8 *code, u32 size, u32 *fsMountArchive, u32 *fsRegisterArchive, u32 *fsTryOpenFile, u32 *fsOpenFileDirectly)
|
2016-11-16 03:41:59 +01:00
|
|
|
{
|
2017-04-28 23:09:33 +02:00
|
|
|
u32 found = 0,
|
|
|
|
*temp = NULL;
|
|
|
|
|
2017-04-16 23:59:20 +02:00
|
|
|
for(u32 addr = 0; addr <= size - 4; addr += 4)
|
2017-04-13 01:03:37 +02:00
|
|
|
{
|
2017-05-06 00:14:33 +02:00
|
|
|
u32 *addr32 = (u32 *)(code + addr);
|
|
|
|
|
|
|
|
switch(*addr32)
|
2017-04-13 01:03:37 +02:00
|
|
|
{
|
2017-04-28 23:09:33 +02:00
|
|
|
case 0xE5970010:
|
2017-05-06 00:14:33 +02:00
|
|
|
if(addr <= size - 12 && *fsMountArchive == 0xFFFFFFFF && addr32[1] == 0xE1CD20D8 && (addr32[2] & 0xFFFFFF) == 0x008D0000) temp = fsMountArchive;
|
2017-04-28 23:09:33 +02:00
|
|
|
break;
|
|
|
|
case 0xE24DD028:
|
2017-05-06 00:14:33 +02:00
|
|
|
if(addr <= size - 16 && *fsMountArchive == 0xFFFFFFFF && addr32[1] == 0xE1A04000 && addr32[2] == 0xE59F60A8 && addr32[3] == 0xE3A0C001) temp = fsMountArchive;
|
2017-04-28 23:09:33 +02:00
|
|
|
break;
|
|
|
|
case 0xE3500008:
|
2017-05-06 00:14:33 +02:00
|
|
|
if(addr <= size - 12 && *fsRegisterArchive == 0xFFFFFFFF && (addr32[1] & 0xFFF00FF0) == 0xE1800400 && (addr32[2] & 0xFFF00FF0) == 0xE1800FC0) temp = fsRegisterArchive;
|
2017-04-28 23:09:33 +02:00
|
|
|
break;
|
|
|
|
case 0xE351003A:
|
2017-05-06 00:14:33 +02:00
|
|
|
if(addr <= size - 0x40 && *fsTryOpenFile == 0xFFFFFFFF && addr32[1] == 0x1AFFFFFC && addr32[0xD] == 0xE590C000 && addr32[0xF] == 0xE12FFF3C) temp = fsTryOpenFile;
|
2017-04-28 23:09:33 +02:00
|
|
|
break;
|
|
|
|
case 0x08030204:
|
|
|
|
if(*fsOpenFileDirectly == 0xFFFFFFFF) temp = fsOpenFileDirectly;
|
|
|
|
break;
|
2017-04-13 01:03:37 +02:00
|
|
|
}
|
2016-11-16 03:41:59 +01:00
|
|
|
|
2017-04-28 23:09:33 +02:00
|
|
|
if(temp != NULL)
|
|
|
|
{
|
|
|
|
*temp = findFunctionStart(code, addr);
|
2016-11-16 03:41:59 +01:00
|
|
|
|
2017-04-28 23:09:33 +02:00
|
|
|
if(*temp != 0xFFFFFFFF)
|
|
|
|
{
|
|
|
|
found++;
|
|
|
|
if(found == 4) break;
|
|
|
|
}
|
2016-11-16 03:41:59 +01:00
|
|
|
|
2017-04-28 23:09:33 +02:00
|
|
|
temp = NULL;
|
|
|
|
}
|
2017-04-16 23:59:20 +02:00
|
|
|
}
|
2017-04-15 22:36:34 +02:00
|
|
|
|
2017-04-28 23:09:33 +02:00
|
|
|
return found == 4;
|
2017-04-16 23:59:20 +02:00
|
|
|
}
|
|
|
|
|
2017-05-05 20:16:44 +02:00
|
|
|
static inline bool findLayeredFsPayloadOffset(u8 *code, u32 size, u32 roSize, u32 dataSize, u32 roAddress, u32 dataAddress, u32 *payloadOffset, u32 *pathOffset, u32 *pathAddress)
|
2017-04-17 01:01:03 +02:00
|
|
|
{
|
2017-05-05 20:16:44 +02:00
|
|
|
u32 roundedTextSize = ((size + 4095) & 0xFFFFF000),
|
|
|
|
roundedRoSize = ((roSize + 4095) & 0xFFFFF000),
|
|
|
|
roundedDataSize = ((dataSize + 4095) & 0xFFFFF000);
|
|
|
|
|
2017-04-17 01:01:03 +02:00
|
|
|
//First check for sufficient padding at the end of the .text segment
|
2017-05-05 20:16:44 +02:00
|
|
|
if(roundedTextSize - size >= romfsredir_bin_size) *payloadOffset = size;
|
|
|
|
else
|
2017-04-17 01:01:03 +02:00
|
|
|
{
|
2017-05-05 20:16:44 +02:00
|
|
|
//If there isn't enough padding look for the "throwFatalError" function to replace
|
|
|
|
u32 svcConnectToPort = 0xFFFFFFFF;
|
2017-04-23 19:20:45 +02:00
|
|
|
|
2017-05-05 20:16:44 +02:00
|
|
|
for(u32 addr = 4; svcConnectToPort == 0xFFFFFFFF && addr <= size - 4; addr += 4)
|
|
|
|
{
|
|
|
|
if(*(u32 *)(code + addr) == 0xEF00002D)
|
|
|
|
svcConnectToPort = addr - 4;
|
|
|
|
}
|
2016-11-16 03:41:59 +01:00
|
|
|
|
2017-05-05 20:16:44 +02:00
|
|
|
if(svcConnectToPort != 0xFFFFFFFF)
|
|
|
|
{
|
|
|
|
u32 func = 0xFFFFFFFF;
|
2017-04-16 23:59:20 +02:00
|
|
|
|
2017-05-05 20:16:44 +02:00
|
|
|
for(u32 i = 4; func == 0xFFFFFFFF && i <= size - 4; i += 4)
|
|
|
|
{
|
|
|
|
if(*(u32 *)(code + i) != MAKE_BRANCH_LINK(i, svcConnectToPort)) continue;
|
|
|
|
|
|
|
|
func = findFunctionStart(code, i);
|
|
|
|
|
|
|
|
for(u32 pos = func + 4; func != 0xFFFFFFFF && pos <= size - 4 && *(u16 *)(code + pos + 2) != 0xE92D; pos += 4)
|
|
|
|
if(*(u32 *)(code + pos) == 0xE200167E) func = 0xFFFFFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(func != 0xFFFFFFFF)
|
|
|
|
*payloadOffset = func;
|
|
|
|
}
|
2017-04-16 23:59:20 +02:00
|
|
|
}
|
|
|
|
|
2017-05-05 20:16:44 +02:00
|
|
|
if(roundedRoSize - roSize >= 39)
|
2016-11-16 14:52:50 +01:00
|
|
|
{
|
2017-05-05 20:16:44 +02:00
|
|
|
*pathOffset = roundedTextSize + roSize;
|
|
|
|
*pathAddress = roAddress + roSize;
|
|
|
|
}
|
|
|
|
else if(roundedDataSize - dataSize >= 39)
|
|
|
|
{
|
|
|
|
*pathOffset = roundedTextSize + roundedRoSize + dataSize;
|
|
|
|
*pathAddress = dataAddress + dataSize;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
u32 strSpace = 0xFFFFFFFF;
|
2017-04-13 01:03:37 +02:00
|
|
|
|
2017-05-05 20:16:44 +02:00
|
|
|
for(u32 addr = 0; strSpace == 0xFFFFFFFF && addr <= size - 4; addr += 4)
|
2017-04-13 01:03:37 +02:00
|
|
|
{
|
2017-05-05 20:16:44 +02:00
|
|
|
if(*(u32 *)(code + addr) == 0xE3A00B42)
|
|
|
|
strSpace = findFunctionStart(code, addr);
|
2017-04-13 01:03:37 +02:00
|
|
|
}
|
|
|
|
|
2017-05-05 20:16:44 +02:00
|
|
|
if(strSpace != 0xFFFFFFFF)
|
2017-04-17 01:01:03 +02:00
|
|
|
{
|
2017-05-05 20:16:44 +02:00
|
|
|
*pathOffset = strSpace;
|
|
|
|
*pathAddress = 0x100000 + strSpace;
|
2017-04-16 23:59:20 +02:00
|
|
|
}
|
2016-11-16 03:41:59 +01:00
|
|
|
}
|
|
|
|
|
2017-05-05 20:16:44 +02:00
|
|
|
return *payloadOffset != 0 && *pathOffset != 0;
|
2016-11-16 03:41:59 +01:00
|
|
|
}
|
|
|
|
|
2016-11-19 15:44:10 +01:00
|
|
|
static inline bool applyCodeIpsPatch(u64 progId, u8 *code, u32 size)
|
|
|
|
{
|
|
|
|
/* Here we look for "/luma/titles/[u64 titleID in hex, uppercase]/code.ips"
|
|
|
|
If it exists it should be an IPS format patch */
|
|
|
|
|
|
|
|
char path[] = "/luma/titles/0000000000000000/code.ips";
|
|
|
|
progIdToStr(path + 28, progId);
|
|
|
|
|
|
|
|
IFile file;
|
|
|
|
|
|
|
|
if(!openLumaFile(&file, path)) return true;
|
|
|
|
|
|
|
|
bool ret = false;
|
|
|
|
u8 buffer[5];
|
|
|
|
u64 total;
|
|
|
|
|
|
|
|
if(R_FAILED(IFile_Read(&file, &total, buffer, 5)) || total != 5 || memcmp(buffer, "PATCH", 5) != 0) goto exit;
|
|
|
|
|
|
|
|
while(R_SUCCEEDED(IFile_Read(&file, &total, buffer, 3)) && total == 3)
|
|
|
|
{
|
|
|
|
if(memcmp(buffer, "EOF", 3) == 0)
|
|
|
|
{
|
|
|
|
ret = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 offset = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2];
|
|
|
|
|
|
|
|
if(R_FAILED(IFile_Read(&file, &total, buffer, 2)) || total != 2) break;
|
|
|
|
|
|
|
|
u32 patchSize = (buffer[0] << 8) | buffer[1];
|
|
|
|
|
|
|
|
if(!patchSize)
|
|
|
|
{
|
|
|
|
if(R_FAILED(IFile_Read(&file, &total, buffer, 2)) || total != 2) break;
|
|
|
|
|
|
|
|
u32 rleSize = (buffer[0] << 8) | buffer[1];
|
|
|
|
|
|
|
|
if(offset + rleSize > size) break;
|
|
|
|
|
|
|
|
if(R_FAILED(IFile_Read(&file, &total, buffer, 1)) || total != 1) break;
|
|
|
|
|
|
|
|
for(u32 i = 0; i < rleSize; i++)
|
|
|
|
code[offset + i] = buffer[0];
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(offset + patchSize > size) break;
|
|
|
|
|
|
|
|
if(R_FAILED(IFile_Read(&file, &total, code + offset, patchSize)) || total != patchSize) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
exit:
|
|
|
|
IFile_Close(&file);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-11-16 03:41:59 +01:00
|
|
|
static inline bool loadTitleCodeSection(u64 progId, u8 *code, u32 size)
|
|
|
|
{
|
|
|
|
/* Here we look for "/luma/titles/[u64 titleID in hex, uppercase]/code.bin"
|
|
|
|
If it exists it should be a decrypted and decompressed binary code file */
|
|
|
|
|
|
|
|
char path[] = "/luma/titles/0000000000000000/code.bin";
|
|
|
|
progIdToStr(path + 28, progId);
|
|
|
|
|
|
|
|
IFile file;
|
|
|
|
|
2016-11-17 15:38:28 +01:00
|
|
|
if(!openLumaFile(&file, path)) return true;
|
2016-11-16 03:41:59 +01:00
|
|
|
|
|
|
|
bool ret;
|
|
|
|
u64 fileSize;
|
|
|
|
|
|
|
|
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize > size) ret = false;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
u64 total;
|
|
|
|
|
|
|
|
ret = R_SUCCEEDED(IFile_Read(&file, &total, code, fileSize)) && total == fileSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
IFile_Close(&file);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
|
|
|
|
{
|
|
|
|
/* Here we look for "/luma/titles/[u64 titleID in hex, uppercase]/locale.txt"
|
|
|
|
If it exists it should contain, for example, "EUR IT" */
|
|
|
|
|
|
|
|
char path[] = "/luma/titles/0000000000000000/locale.txt";
|
|
|
|
progIdToStr(path + 28, progId);
|
|
|
|
|
|
|
|
IFile file;
|
|
|
|
|
2016-11-17 15:38:28 +01:00
|
|
|
if(!openLumaFile(&file, path)) return true;
|
2016-11-16 03:41:59 +01:00
|
|
|
|
2016-11-16 14:52:50 +01:00
|
|
|
bool ret = false;
|
2016-11-16 03:41:59 +01:00
|
|
|
u64 fileSize;
|
|
|
|
|
2016-11-16 14:52:50 +01:00
|
|
|
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize < 6 || fileSize > 8) goto exit;
|
2016-11-16 03:41:59 +01:00
|
|
|
|
|
|
|
char buf[8];
|
|
|
|
u64 total;
|
|
|
|
|
2016-11-16 20:06:01 +01:00
|
|
|
if(R_FAILED(IFile_Read(&file, &total, buf, fileSize))) goto exit;
|
2016-11-16 03:41:59 +01:00
|
|
|
|
2017-04-28 23:09:33 +02:00
|
|
|
static const char *regions[] = {"JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"},
|
|
|
|
*languages[] = {"JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"};
|
2016-11-16 03:41:59 +01:00
|
|
|
|
2017-04-28 23:09:33 +02:00
|
|
|
u32 i;
|
2016-11-16 03:41:59 +01:00
|
|
|
|
2017-04-28 23:09:33 +02:00
|
|
|
for(i = 0; i < sizeof(regions) / sizeof(char *); i++)
|
|
|
|
{
|
2016-11-16 03:41:59 +01:00
|
|
|
if(memcmp(buf, regions[i], 3) == 0)
|
|
|
|
{
|
|
|
|
*regionId = (u8)i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-28 23:09:33 +02:00
|
|
|
if(i != sizeof(regions) / sizeof(char *))
|
2016-11-16 03:41:59 +01:00
|
|
|
{
|
2017-04-28 23:09:33 +02:00
|
|
|
for(i = 0; i < sizeof(languages) / sizeof(char *); i++)
|
2016-11-16 03:41:59 +01:00
|
|
|
{
|
2017-04-28 23:09:33 +02:00
|
|
|
if(memcmp(buf + 4, languages[i], 2) == 0)
|
|
|
|
{
|
|
|
|
*languageId = (u8)i;
|
|
|
|
ret = true;
|
|
|
|
break;
|
|
|
|
}
|
2016-11-16 03:41:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
exit:
|
|
|
|
IFile_Close(&file);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-05-05 20:16:44 +02:00
|
|
|
static inline bool patchLayeredFs(u64 progId, u8 *code, u32 size, u32 textSize, u32 roSize, u32 dataSize, u32 roAddress, u32 dataAddress)
|
2016-11-16 03:41:59 +01:00
|
|
|
{
|
|
|
|
/* Here we look for "/luma/titles/[u64 titleID in hex, uppercase]/romfs"
|
2017-04-13 03:38:36 +02:00
|
|
|
If it exists it should be a folder containing ROMFS files */
|
2016-11-16 03:41:59 +01:00
|
|
|
|
|
|
|
char path[] = "/luma/titles/0000000000000000/romfs";
|
|
|
|
progIdToStr(path + 28, progId);
|
|
|
|
|
2017-04-23 03:13:38 +02:00
|
|
|
u32 archiveId = checkLumaDir(path);
|
2017-04-13 02:49:19 +02:00
|
|
|
|
2017-04-23 03:13:38 +02:00
|
|
|
if(!archiveId) return true;
|
2017-04-13 02:49:19 +02:00
|
|
|
|
2017-04-13 01:03:37 +02:00
|
|
|
u32 fsMountArchive = 0xFFFFFFFF,
|
|
|
|
fsRegisterArchive = 0xFFFFFFFF,
|
|
|
|
fsTryOpenFile = 0xFFFFFFFF,
|
2017-04-17 01:01:03 +02:00
|
|
|
fsOpenFileDirectly = 0xFFFFFFFF,
|
2017-05-05 20:16:44 +02:00
|
|
|
payloadOffset = 0,
|
|
|
|
pathOffset = 0,
|
|
|
|
pathAddress;
|
2016-11-16 03:41:59 +01:00
|
|
|
|
2017-04-29 19:15:41 +02:00
|
|
|
if(!findLayeredFsSymbols(code, textSize, &fsMountArchive, &fsRegisterArchive, &fsTryOpenFile, &fsOpenFileDirectly) ||
|
2017-05-05 20:16:44 +02:00
|
|
|
!findLayeredFsPayloadOffset(code, textSize, roSize, dataSize, roAddress, dataAddress, &payloadOffset, &pathOffset, &pathAddress)) return false;
|
2017-04-29 19:15:41 +02:00
|
|
|
|
2017-05-05 20:16:44 +02:00
|
|
|
static const char *updateRomFsMounts[] = { "rom2:",
|
2017-05-06 15:48:38 +02:00
|
|
|
"rex:",
|
2017-05-05 20:16:44 +02:00
|
|
|
"patch:",
|
|
|
|
"ext:",
|
|
|
|
"rom:" };
|
|
|
|
u32 updateRomFsIndex;
|
2017-04-29 19:15:41 +02:00
|
|
|
|
|
|
|
//Locate update RomFSes
|
2017-05-05 20:16:44 +02:00
|
|
|
for(updateRomFsIndex = 0; updateRomFsIndex < sizeof(updateRomFsMounts) / sizeof(char *) - 1; updateRomFsIndex++)
|
2017-05-08 21:35:46 +02:00
|
|
|
{
|
|
|
|
u32 patternSize = strnlen(updateRomFsMounts[updateRomFsIndex], 255);
|
|
|
|
u8 temp[7];
|
|
|
|
temp[0] = 0;
|
|
|
|
memcpy(temp + 1, updateRomFsMounts[updateRomFsIndex], patternSize);
|
|
|
|
if(memsearch(code, temp, size, patternSize + 1) != NULL) break;
|
|
|
|
}
|
2016-11-16 03:41:59 +01:00
|
|
|
|
|
|
|
//Setup the payload
|
2017-04-17 01:01:03 +02:00
|
|
|
u8 *payload = code + payloadOffset;
|
2016-11-17 15:38:28 +01:00
|
|
|
memcpy(payload, romfsredir_bin, romfsredir_bin_size);
|
|
|
|
|
2017-04-13 01:03:37 +02:00
|
|
|
//Insert symbols in the payload
|
|
|
|
u32 *payload32 = (u32 *)payload;
|
2017-05-05 20:16:44 +02:00
|
|
|
for(u32 i = 0; i < romfsredir_bin_size / 4; i++)
|
2016-11-16 03:41:59 +01:00
|
|
|
{
|
2017-04-28 23:09:33 +02:00
|
|
|
switch(payload32[i])
|
2017-04-13 01:03:37 +02:00
|
|
|
{
|
|
|
|
case 0xdead0000:
|
|
|
|
payload32[i] = *(u32 *)(code + fsOpenFileDirectly);
|
|
|
|
break;
|
|
|
|
case 0xdead0001:
|
2017-04-17 01:01:03 +02:00
|
|
|
payload32[i] = MAKE_BRANCH(payloadOffset + i * 4, fsOpenFileDirectly + 4);
|
2017-04-13 01:03:37 +02:00
|
|
|
break;
|
|
|
|
case 0xdead0002:
|
|
|
|
payload32[i] = *(u32 *)(code + fsTryOpenFile);
|
|
|
|
break;
|
|
|
|
case 0xdead0003:
|
2017-04-17 01:01:03 +02:00
|
|
|
payload32[i] = MAKE_BRANCH(payloadOffset + i * 4, fsTryOpenFile + 4);
|
2017-04-13 01:03:37 +02:00
|
|
|
break;
|
|
|
|
case 0xdead0004:
|
2017-05-05 20:16:44 +02:00
|
|
|
payload32[i] = pathAddress;
|
2017-04-13 01:03:37 +02:00
|
|
|
break;
|
|
|
|
case 0xdead0005:
|
|
|
|
payload32[i] = 0x100000 + fsMountArchive;
|
|
|
|
break;
|
|
|
|
case 0xdead0006:
|
|
|
|
payload32[i] = 0x100000 + fsRegisterArchive;
|
|
|
|
break;
|
2017-04-13 02:49:19 +02:00
|
|
|
case 0xdead0007:
|
2017-04-23 03:13:38 +02:00
|
|
|
payload32[i] = archiveId;
|
2017-04-13 02:49:19 +02:00
|
|
|
break;
|
2017-04-29 19:15:41 +02:00
|
|
|
case 0xdead0008:
|
2017-05-05 20:16:44 +02:00
|
|
|
memcpy(payload32 + i, updateRomFsMounts[updateRomFsIndex], 4);
|
2017-04-29 19:15:41 +02:00
|
|
|
break;
|
2017-04-13 01:03:37 +02:00
|
|
|
}
|
2016-11-16 03:41:59 +01:00
|
|
|
}
|
|
|
|
|
2017-05-05 20:16:44 +02:00
|
|
|
memcpy(code + pathOffset, "lf:", 3);
|
|
|
|
memcpy(code + pathOffset + 3, path, sizeof(path));
|
|
|
|
|
2017-04-13 01:03:37 +02:00
|
|
|
//Place the hooks
|
2017-04-17 01:01:03 +02:00
|
|
|
*(u32 *)(code + fsOpenFileDirectly) = MAKE_BRANCH(fsOpenFileDirectly, payloadOffset);
|
|
|
|
*(u32 *)(code + fsTryOpenFile) = MAKE_BRANCH(fsTryOpenFile, payloadOffset + 12);
|
2016-11-16 03:41:59 +01:00
|
|
|
|
2017-04-13 01:03:37 +02:00
|
|
|
return true;
|
2016-11-16 03:41:59 +01:00
|
|
|
}
|
|
|
|
|
2017-05-05 20:16:44 +02:00
|
|
|
void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 roSize, u32 dataSize, u32 roAddress, u32 dataAddress)
|
2016-04-02 17:58:06 +02:00
|
|
|
{
|
2016-08-17 23:47:30 +02:00
|
|
|
loadCFWInfo();
|
2016-08-26 18:44:39 +02:00
|
|
|
|
2017-06-02 03:39:26 +02:00
|
|
|
if(progId == 0x0004003000008F02LL || //USA Home Menu
|
|
|
|
progId == 0x0004003000008202LL || //JPN Home Menu
|
|
|
|
progId == 0x0004003000009802LL || //EUR Home Menu
|
|
|
|
progId == 0x000400300000A902LL || //KOR Home Menu
|
|
|
|
progId == 0x000400300000A102LL || //CHN Home Menu
|
|
|
|
progId == 0x000400300000B102LL) //TWN Home Menu
|
2016-04-02 17:58:06 +02:00
|
|
|
{
|
2017-06-02 04:35:02 +02:00
|
|
|
bool applyRegionFreePatch = true;
|
2017-06-02 03:39:26 +02:00
|
|
|
|
|
|
|
switch(progId)
|
|
|
|
{
|
|
|
|
case 0x0004003000008F02LL: //USA Home Menu
|
|
|
|
case 0x0004003000008202LL: //JPN Home Menu
|
|
|
|
case 0x0004003000009802LL: //EUR Home Menu
|
|
|
|
if(progVer <= 4) applyRegionFreePatch = false;
|
|
|
|
break;
|
|
|
|
case 0x000400300000A902LL: //KOR Home Menu
|
|
|
|
if(!progVer) applyRegionFreePatch = false;
|
|
|
|
break;
|
|
|
|
}
|
2016-11-03 18:55:40 +01:00
|
|
|
|
2017-06-02 03:39:26 +02:00
|
|
|
if(applyRegionFreePatch)
|
|
|
|
{
|
|
|
|
static const u8 pattern[] = {
|
|
|
|
0x0A, 0x0C, 0x00, 0x10
|
|
|
|
},
|
|
|
|
patch[] = {
|
|
|
|
0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1
|
|
|
|
};
|
|
|
|
|
|
|
|
//Patch SMDH region checks
|
|
|
|
if(!patchMemory(code, textSize,
|
|
|
|
pattern,
|
|
|
|
sizeof(pattern), -31,
|
|
|
|
patch,
|
|
|
|
sizeof(patch), 1
|
|
|
|
)) goto error;
|
|
|
|
}
|
|
|
|
|
2017-06-02 04:35:02 +02:00
|
|
|
static const u8 pattern[] = {
|
|
|
|
0x10, 0xD1, 0xE5, 0x08, 0x00, 0x8D
|
|
|
|
};
|
2017-06-02 03:39:26 +02:00
|
|
|
|
2017-06-02 04:35:02 +02:00
|
|
|
u8 *temp = memsearch(code, pattern, textSize, sizeof(pattern));
|
2017-06-02 03:39:26 +02:00
|
|
|
|
2017-06-02 04:35:02 +02:00
|
|
|
if(temp == NULL) goto error;
|
|
|
|
|
|
|
|
u32 additive = findFunctionStart(code, (u32)(temp - code - 1));
|
|
|
|
|
|
|
|
if(additive == 0xFFFFFFFF) goto error;
|
|
|
|
|
|
|
|
u32 *off = (u32 *)(code + additive);
|
|
|
|
|
|
|
|
off[0] = 0xE3A00000; //mov r0, #0
|
|
|
|
off[1] = 0xE12FFF1E; //bx lr
|
2016-11-03 18:55:40 +01:00
|
|
|
}
|
2016-03-31 01:38:28 +02:00
|
|
|
|
2016-11-03 19:02:38 +01:00
|
|
|
else if(progId == 0x0004013000003202LL) //FRIENDS
|
2016-11-03 18:55:40 +01:00
|
|
|
{
|
2016-11-06 14:45:45 +01:00
|
|
|
static const u8 pattern[] = {
|
2016-11-03 18:55:40 +01:00
|
|
|
0x42, 0xE0, 0x1E, 0xFF
|
|
|
|
};
|
2016-03-29 22:43:15 +02:00
|
|
|
|
2017-04-11 16:33:40 +02:00
|
|
|
u8 mostRecentFpdVer = 10;
|
2016-09-08 02:12:29 +02:00
|
|
|
|
2017-04-17 02:14:17 +02:00
|
|
|
u8 *off = memsearch(code, pattern, textSize, sizeof(pattern));
|
2016-09-08 02:12:29 +02:00
|
|
|
|
2016-11-15 19:29:48 +01:00
|
|
|
if(off == NULL) goto error;
|
2016-04-08 23:28:40 +02:00
|
|
|
|
2016-11-03 18:55:40 +01:00
|
|
|
//Allow online access to work with old friends modules
|
2016-11-15 19:29:48 +01:00
|
|
|
if(off[0xA] < mostRecentFpdVer) off[0xA] = mostRecentFpdVer;
|
2016-11-03 18:55:40 +01:00
|
|
|
}
|
2016-10-10 15:53:56 +02:00
|
|
|
|
2016-11-03 19:02:38 +01:00
|
|
|
else if((progId == 0x0004001000021000LL || //USA MSET
|
|
|
|
progId == 0x0004001000020000LL || //JPN MSET
|
|
|
|
progId == 0x0004001000022000LL || //EUR MSET
|
|
|
|
progId == 0x0004001000026000LL || //CHN MSET
|
|
|
|
progId == 0x0004001000027000LL || //KOR MSET
|
|
|
|
progId == 0x0004001000028000LL) //TWN MSET
|
2017-03-05 14:03:15 +01:00
|
|
|
&& CONFIG(PATCHVERSTRING))
|
2016-11-03 18:55:40 +01:00
|
|
|
{
|
2016-11-06 14:45:45 +01:00
|
|
|
static const u16 pattern[] = u"Ve";
|
|
|
|
static u16 *patch;
|
|
|
|
u32 patchSize = 0,
|
2016-11-03 18:55:40 +01:00
|
|
|
currentNand = BOOTCFG_NAND;
|
2016-04-08 23:28:40 +02:00
|
|
|
|
2016-11-03 18:55:40 +01:00
|
|
|
u16 customVerString[19];
|
2016-11-06 14:45:45 +01:00
|
|
|
loadCustomVerString(customVerString, &patchSize, currentNand);
|
2016-10-10 17:30:53 +02:00
|
|
|
|
2016-11-06 14:45:45 +01:00
|
|
|
if(patchSize != 0) patch = customVerString;
|
2016-11-03 18:55:40 +01:00
|
|
|
else
|
2016-03-29 22:43:15 +02:00
|
|
|
{
|
2016-11-06 14:45:45 +01:00
|
|
|
patchSize = 8;
|
2016-11-03 18:55:40 +01:00
|
|
|
u32 currentFirm = BOOTCFG_FIRM;
|
|
|
|
|
|
|
|
static u16 *verStringsNands[] = { u" Sys",
|
|
|
|
u" Emu",
|
|
|
|
u"Emu2",
|
|
|
|
u"Emu3",
|
|
|
|
u"Emu4" },
|
|
|
|
|
|
|
|
*verStringsEmuSys[] = { u"EmuS",
|
|
|
|
u"Em2S",
|
|
|
|
u"Em3S",
|
|
|
|
u"Em4S" },
|
|
|
|
|
|
|
|
*verStringsSysEmu[] = { u"SysE",
|
|
|
|
u"SyE2",
|
|
|
|
u"SyE3",
|
|
|
|
u"SyE4" };
|
|
|
|
|
2016-11-06 14:45:45 +01:00
|
|
|
patch = (currentFirm != 0) == (currentNand != 0) ? verStringsNands[currentNand] :
|
|
|
|
(!currentNand ? verStringsSysEmu[currentFirm - 1] : verStringsEmuSys[currentNand - 1]);
|
2016-03-29 22:43:15 +02:00
|
|
|
}
|
|
|
|
|
2016-11-03 18:55:40 +01:00
|
|
|
//Patch Ver. string
|
2017-04-17 02:14:17 +02:00
|
|
|
if(!patchMemory(code, textSize,
|
2016-11-06 14:45:45 +01:00
|
|
|
pattern,
|
|
|
|
sizeof(pattern) - 2, 0,
|
2016-11-06 14:52:10 +01:00
|
|
|
patch,
|
2016-11-06 14:45:45 +01:00
|
|
|
patchSize, 1
|
2016-11-15 19:29:48 +01:00
|
|
|
)) goto error;
|
2016-11-03 18:55:40 +01:00
|
|
|
}
|
|
|
|
|
2016-11-03 19:02:38 +01:00
|
|
|
else if(progId == 0x0004013000008002LL) //NS
|
2016-11-03 18:55:40 +01:00
|
|
|
{
|
2016-11-03 19:36:08 +01:00
|
|
|
if(progVer > 4)
|
2016-03-29 22:43:15 +02:00
|
|
|
{
|
2016-11-06 14:45:45 +01:00
|
|
|
static const u8 pattern[] = {
|
2016-03-29 22:43:15 +02:00
|
|
|
0x0C, 0x18, 0xE1, 0xD8
|
2016-10-10 17:30:53 +02:00
|
|
|
},
|
2016-11-06 14:45:45 +01:00
|
|
|
patch[] = {
|
2016-03-29 22:43:15 +02:00
|
|
|
0x0B, 0x18, 0x21, 0xC8
|
|
|
|
};
|
|
|
|
|
2016-04-02 18:48:31 +02:00
|
|
|
//Disable updates from foreign carts (makes carts region-free)
|
2017-04-17 02:14:17 +02:00
|
|
|
u32 ret = patchMemory(code, textSize,
|
2016-11-06 14:45:45 +01:00
|
|
|
pattern,
|
|
|
|
sizeof(pattern), 0,
|
2016-11-06 14:52:10 +01:00
|
|
|
patch,
|
2016-11-06 14:45:45 +01:00
|
|
|
sizeof(patch), 2
|
|
|
|
);
|
|
|
|
|
2016-11-15 19:29:48 +01:00
|
|
|
if(ret == 0 || (ret == 1 && progVer > 0xB)) goto error;
|
2016-11-03 18:55:40 +01:00
|
|
|
}
|
2016-04-02 17:58:06 +02:00
|
|
|
|
2016-11-03 18:55:40 +01:00
|
|
|
if(LOADERFLAG(ISN3DS))
|
|
|
|
{
|
2016-09-11 19:17:56 +02:00
|
|
|
u32 cpuSetting = MULTICONFIG(NEWCPU);
|
2016-04-19 17:17:39 +02:00
|
|
|
|
2016-09-08 02:12:29 +02:00
|
|
|
if(cpuSetting != 0)
|
2016-04-12 15:25:36 +02:00
|
|
|
{
|
2016-11-06 14:52:10 +01:00
|
|
|
static const u8 pattern[] = {
|
2016-11-03 18:55:40 +01:00
|
|
|
0x0C, 0x00, 0x94, 0x15
|
2016-04-12 15:25:36 +02:00
|
|
|
};
|
|
|
|
|
2017-04-17 02:14:17 +02:00
|
|
|
u32 *off = (u32 *)memsearch(code, pattern, textSize, sizeof(pattern));
|
2016-04-12 15:25:36 +02:00
|
|
|
|
2016-11-15 19:29:48 +01:00
|
|
|
if(off == NULL) goto error;
|
|
|
|
|
|
|
|
//Patch N3DS CPU Clock and L2 cache setting
|
|
|
|
*(off - 4) = *(off - 3);
|
|
|
|
*(off - 3) = *(off - 1);
|
|
|
|
memcpy(off - 1, off, 16);
|
|
|
|
*(off + 3) = 0xE3800000 | cpuSetting;
|
2016-04-12 15:25:36 +02:00
|
|
|
}
|
2016-03-29 17:43:53 +02:00
|
|
|
}
|
2016-11-03 18:55:40 +01:00
|
|
|
}
|
2016-03-29 22:43:15 +02:00
|
|
|
|
2016-11-03 19:02:38 +01:00
|
|
|
else if(progId == 0x0004013000001702LL) //CFG
|
2016-11-03 18:55:40 +01:00
|
|
|
{
|
2016-11-06 14:45:45 +01:00
|
|
|
static const u8 pattern[] = {
|
2016-11-03 18:55:40 +01:00
|
|
|
0x06, 0x46, 0x10, 0x48
|
|
|
|
},
|
2016-11-06 14:45:45 +01:00
|
|
|
patch[] = {
|
2016-11-03 18:55:40 +01:00
|
|
|
0x00, 0x26
|
|
|
|
};
|
|
|
|
|
|
|
|
//Disable SecureInfo signature check
|
2017-04-17 02:14:17 +02:00
|
|
|
if(!patchMemory(code, textSize,
|
2016-11-06 14:45:45 +01:00
|
|
|
pattern,
|
|
|
|
sizeof(pattern), 0,
|
2016-11-06 14:52:10 +01:00
|
|
|
patch,
|
2016-11-06 14:45:45 +01:00
|
|
|
sizeof(patch), 1
|
2016-11-15 19:29:48 +01:00
|
|
|
)) goto error;
|
2016-11-03 18:55:40 +01:00
|
|
|
|
|
|
|
if(secureInfoExists())
|
2016-03-29 22:43:15 +02:00
|
|
|
{
|
2016-11-06 14:45:45 +01:00
|
|
|
static const u16 pattern[] = u"Sec",
|
|
|
|
patch[] = u"C";
|
2016-03-29 22:43:15 +02:00
|
|
|
|
2016-11-03 18:55:40 +01:00
|
|
|
//Use SecureInfo_C
|
2017-05-05 20:16:44 +02:00
|
|
|
if(patchMemory(code + ((textSize + 4095) & 0xFFFFF000), roSize,
|
2016-11-06 14:45:45 +01:00
|
|
|
pattern,
|
|
|
|
sizeof(pattern) - 2, 22,
|
2016-11-06 14:52:10 +01:00
|
|
|
patch,
|
2016-11-06 14:45:45 +01:00
|
|
|
sizeof(patch) - 2, 2
|
2016-11-15 19:29:48 +01:00
|
|
|
) != 2) goto error;
|
2016-05-14 15:35:59 +02:00
|
|
|
}
|
2016-11-03 18:55:40 +01:00
|
|
|
}
|
2016-11-05 17:46:47 +01:00
|
|
|
|
2016-11-03 19:02:38 +01:00
|
|
|
else if(progId == 0x0004013000003702LL && progVer > 0) //RO
|
2016-11-03 18:55:40 +01:00
|
|
|
{
|
2016-11-06 14:45:45 +01:00
|
|
|
static const u8 pattern[] = {
|
2016-11-03 18:55:40 +01:00
|
|
|
0x20, 0xA0, 0xE1, 0x8B
|
|
|
|
},
|
2016-11-06 14:45:45 +01:00
|
|
|
pattern2[] = {
|
2016-11-03 18:55:40 +01:00
|
|
|
0xE1, 0x30, 0x40, 0x2D
|
|
|
|
},
|
2016-11-06 14:45:45 +01:00
|
|
|
pattern3[] = {
|
2016-11-03 18:55:40 +01:00
|
|
|
0x2D, 0xE9, 0x01, 0x70
|
|
|
|
},
|
2016-11-06 14:45:45 +01:00
|
|
|
patch[] = {
|
2016-11-03 19:02:38 +01:00
|
|
|
0x00, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1 //mov r0, #0; bx lr
|
2016-11-03 18:55:40 +01:00
|
|
|
};
|
|
|
|
|
2016-11-15 19:29:48 +01:00
|
|
|
//Disable CRR0 signature (RSA2048 with SHA256) check and CRO0/CRR0 SHA256 hash checks (section hashes, and hash table)
|
2017-04-17 02:14:17 +02:00
|
|
|
if(!patchMemory(code, textSize,
|
2016-11-06 14:45:45 +01:00
|
|
|
pattern,
|
|
|
|
sizeof(pattern), -9,
|
2016-11-06 14:52:10 +01:00
|
|
|
patch,
|
2016-11-06 14:45:45 +01:00
|
|
|
sizeof(patch), 1
|
2016-11-15 19:29:48 +01:00
|
|
|
) ||
|
2017-04-17 02:14:17 +02:00
|
|
|
!patchMemory(code, textSize,
|
2016-11-06 14:45:45 +01:00
|
|
|
pattern2,
|
|
|
|
sizeof(pattern2), 1,
|
2016-11-06 14:52:10 +01:00
|
|
|
patch,
|
2016-11-06 14:45:45 +01:00
|
|
|
sizeof(patch), 1
|
2016-11-15 19:29:48 +01:00
|
|
|
) ||
|
2017-04-17 02:14:17 +02:00
|
|
|
!patchMemory(code, textSize,
|
2016-11-06 14:45:45 +01:00
|
|
|
pattern3,
|
|
|
|
sizeof(pattern3), -2,
|
2016-11-06 14:52:10 +01:00
|
|
|
patch,
|
2016-11-06 14:45:45 +01:00
|
|
|
sizeof(patch), 1
|
2016-11-15 19:29:48 +01:00
|
|
|
)) goto error;
|
2016-11-03 18:55:40 +01:00
|
|
|
}
|
2016-10-10 17:30:53 +02:00
|
|
|
|
2017-04-15 15:47:10 +02:00
|
|
|
else if(progId == 0x0004003000008A02LL && CONFIG(ENABLEEXCEPTIONHANDLERS) && !CONFIG(PATCHUNITINFO)) //ErrDisp
|
2016-11-03 18:55:40 +01:00
|
|
|
{
|
2017-03-05 14:03:15 +01:00
|
|
|
static const u8 pattern[] = {
|
2016-11-03 18:55:40 +01:00
|
|
|
0x00, 0xD0, 0xE5, 0xDB
|
|
|
|
},
|
2016-11-06 14:45:45 +01:00
|
|
|
pattern2[] = {
|
2016-11-03 18:55:40 +01:00
|
|
|
0x14, 0x00, 0xD0, 0xE5, 0x01
|
|
|
|
},
|
2016-11-06 14:45:45 +01:00
|
|
|
patch[] = {
|
2016-11-03 18:55:40 +01:00
|
|
|
0x00, 0x00, 0xA0, 0xE3
|
|
|
|
};
|
|
|
|
|
2016-11-06 14:45:45 +01:00
|
|
|
//Patch UNITINFO checks to make ErrDisp more verbose
|
2017-04-17 02:14:17 +02:00
|
|
|
if(!patchMemory(code, textSize,
|
2016-11-06 14:45:45 +01:00
|
|
|
pattern,
|
|
|
|
sizeof(pattern), -1,
|
2016-11-06 14:52:10 +01:00
|
|
|
patch,
|
2016-11-06 14:45:45 +01:00
|
|
|
sizeof(patch), 1
|
2016-11-15 19:29:48 +01:00
|
|
|
) ||
|
2017-04-17 02:14:17 +02:00
|
|
|
patchMemory(code, textSize,
|
2016-11-06 14:45:45 +01:00
|
|
|
pattern2,
|
|
|
|
sizeof(pattern2), 0,
|
2016-11-06 14:52:10 +01:00
|
|
|
patch,
|
2016-11-06 14:45:45 +01:00
|
|
|
sizeof(patch), 3
|
2016-11-15 19:29:48 +01:00
|
|
|
) != 3) goto error;
|
2016-11-03 18:55:40 +01:00
|
|
|
}
|
2016-10-10 17:30:53 +02:00
|
|
|
|
2017-04-15 15:47:10 +02:00
|
|
|
else if(progId == 0x0004013000002802LL) //DLP
|
2016-12-22 17:51:17 +01:00
|
|
|
{
|
|
|
|
static const u8 pattern[] = {
|
|
|
|
0x0C, 0xAC, 0xC0, 0xD8
|
|
|
|
},
|
|
|
|
patch[] = {
|
|
|
|
0x00, 0x00, 0x00, 0x00
|
|
|
|
};
|
2016-12-22 17:53:31 +01:00
|
|
|
|
|
|
|
//Patch DLP region checks
|
2017-04-17 02:14:17 +02:00
|
|
|
if(!patchMemory(code, textSize,
|
2016-12-22 17:51:17 +01:00
|
|
|
pattern,
|
|
|
|
sizeof(pattern), 0,
|
|
|
|
patch,
|
|
|
|
sizeof(patch), 1
|
|
|
|
)) goto error;
|
2016-12-22 17:53:31 +01:00
|
|
|
}
|
2017-03-05 14:03:15 +01:00
|
|
|
|
2017-05-01 18:21:18 +02:00
|
|
|
if(CONFIG(PATCHGAMES))
|
2016-11-03 18:55:40 +01:00
|
|
|
{
|
2016-11-16 03:41:59 +01:00
|
|
|
if(!loadTitleCodeSection(progId, code, size) ||
|
2017-05-01 18:21:18 +02:00
|
|
|
!applyCodeIpsPatch(progId, code, size)) goto error;
|
2016-09-06 13:43:00 +02:00
|
|
|
|
2017-05-01 18:21:18 +02:00
|
|
|
if((u32)((progId >> 0x20) & 0xFFFFFFEDULL) == 0x00040000)
|
2016-05-28 16:13:22 +02:00
|
|
|
{
|
2017-05-01 18:21:18 +02:00
|
|
|
u8 regionId = 0xFF,
|
2017-05-06 00:14:33 +02:00
|
|
|
languageId;
|
2017-05-01 18:21:18 +02:00
|
|
|
|
|
|
|
if(!loadTitleLocaleConfig(progId, ®ionId, &languageId) ||
|
2017-05-05 20:20:07 +02:00
|
|
|
!patchLayeredFs(progId, code, size, textSize, roSize, dataSize, roAddress, dataAddress)) goto error;
|
Added region/language emulation feature, thanks to the hard work of @TuxSH
Create a "locales" folder inside aurei, and one .txt for each game, with the title id of the game. The txt must be exactly 6 bytes long: 3 characters for the region ("JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"), a space, and two for the language ("JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"). You can enable the feature globally in the config menu. This should also make DLCs for foreign games work.
2016-04-14 00:46:36 +02:00
|
|
|
|
2017-05-01 18:21:18 +02:00
|
|
|
if(regionId != 0xFF)
|
|
|
|
{
|
|
|
|
u32 CFGUHandleOffset;
|
|
|
|
u8 *CFGU_GetConfigInfoBlk2_endPos = getCfgOffsets(code, textSize, &CFGUHandleOffset);
|
2016-11-15 19:29:48 +01:00
|
|
|
|
2017-05-01 18:21:18 +02:00
|
|
|
if(CFGU_GetConfigInfoBlk2_endPos == NULL ||
|
|
|
|
!patchCfgGetLanguage(code, textSize, languageId, CFGU_GetConfigInfoBlk2_endPos)) goto error;
|
2016-11-15 19:29:48 +01:00
|
|
|
|
2017-05-01 18:21:18 +02:00
|
|
|
patchCfgGetRegion(code, textSize, regionId, CFGUHandleOffset);
|
|
|
|
}
|
2016-11-03 18:55:40 +01:00
|
|
|
}
|
2016-03-29 17:43:53 +02:00
|
|
|
}
|
2016-11-06 14:45:45 +01:00
|
|
|
|
2016-11-15 19:29:48 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
error:
|
|
|
|
svcBreak(USERBREAK_ASSERT);
|
|
|
|
while(true);
|
2016-12-22 17:51:17 +01:00
|
|
|
}
|