2018-05-09 02:34:44 +02:00
|
|
|
/* Patch for MCP_LoadFile (ioctl 0x53).
|
2018-06-01 13:09:51 +02:00
|
|
|
* Also adds a sibling ioctl, 0x64, that allows setting a custom path to a main RPX
|
2018-05-09 02:34:44 +02:00
|
|
|
*
|
|
|
|
* Reference for most of the types and whatever:
|
|
|
|
* https://github.com/exjam/decaf-emu/tree/ios/src/libdecaf/src/ios/mcp
|
|
|
|
*
|
|
|
|
* This is a proof of concept, and shouldn't be used until it's cleaned up a bit.
|
2018-05-10 13:20:53 +02:00
|
|
|
* Co-authored by exjam, Maschell and QuarkTheAwesome
|
2018-05-09 02:34:44 +02:00
|
|
|
*
|
|
|
|
* Flow of calls:
|
|
|
|
* - kernel loads system libraries, app rpx or user calls OSDynLoad
|
|
|
|
* - goes to loader.elf, which will call ioctl 0x53
|
|
|
|
* - with fileType = LOAD_FILE_CAFE_OS
|
|
|
|
* - on failure, again with LOAD_FILE_PROCESS_CODE
|
|
|
|
* - on failure, again with LOAD_FILE_SYS_DATA_CODE
|
|
|
|
* - each request routes here where we can do whatever
|
|
|
|
*/
|
|
|
|
|
2018-05-10 13:20:53 +02:00
|
|
|
#include "logger.h"
|
2018-05-09 02:34:44 +02:00
|
|
|
#include "ipc_types.h"
|
2019-08-15 10:29:36 +02:00
|
|
|
#include "fsa.h"
|
|
|
|
#include "svc.h"
|
2018-06-01 13:09:51 +02:00
|
|
|
#include <string.h>
|
2018-05-09 02:34:44 +02:00
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
//Load from the process's code directory (process title id)/code/%s
|
|
|
|
LOAD_FILE_PROCESS_CODE = 0,
|
|
|
|
//Load from the CafeOS directory (00050010-1000400A)/code/%s
|
|
|
|
LOAD_FILE_CAFE_OS = 1,
|
|
|
|
//Load from a system data title's content directory (0005001B-x)/content/%s
|
|
|
|
LOAD_FILE_SYS_DATA_CONTENT = 2,
|
|
|
|
//Load from a system data title's code directory (0005001B-x)/content/%s
|
|
|
|
LOAD_FILE_SYS_DATA_CODE = 3,
|
2018-05-11 08:01:06 +02:00
|
|
|
|
|
|
|
LOAD_FILE_FORCE_SIZE = 0xFFFFFFFF,
|
2018-05-09 02:34:44 +02:00
|
|
|
} MCPFileType;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
unsigned char unk[0x10];
|
|
|
|
|
|
|
|
unsigned int pos;
|
|
|
|
MCPFileType type;
|
|
|
|
unsigned int cafe_pid;
|
|
|
|
|
|
|
|
unsigned char unk2[0xC];
|
|
|
|
|
|
|
|
char name[0x40];
|
|
|
|
|
|
|
|
unsigned char unk3[0x12D8 - 0x68];
|
2018-05-10 13:20:53 +02:00
|
|
|
} MCPLoadFileRequest;
|
2018-05-09 02:34:44 +02:00
|
|
|
//sizeof(MCPLoadFileRequest) = 0x12D8
|
|
|
|
|
|
|
|
int (*const real_MCP_LoadFile)(ipcmessage* msg) = (void*)0x0501CAA8 + 1; //+1 for thumb
|
2018-05-10 13:20:53 +02:00
|
|
|
int (*const MCP_DoLoadFile)(const char* path, const char* path2, void* outputBuffer, u32 outLength, u32 pos, int* bytesRead, u32 unk) = (void*)0x05017248 + 1;
|
|
|
|
int (*const MCP_UnknownStuff)(const char* path, u32 pos, void* outputBuffer, u32 outLength, u32 outLength2, u32 unk) = (void*)0x05014CAC + 1;
|
2018-05-09 02:34:44 +02:00
|
|
|
|
2018-06-04 11:54:50 +02:00
|
|
|
static int MCP_LoadCustomFile(char* path, ipcmessage* msg, MCPLoadFileRequest* request);
|
2019-08-15 10:29:36 +02:00
|
|
|
static bool skipPPCSetup = false;
|
2018-06-01 13:09:51 +02:00
|
|
|
static bool didrpxfirstchunk = false;
|
|
|
|
static char rpxpath[0x280];
|
|
|
|
|
2018-06-04 11:54:50 +02:00
|
|
|
#define log(fmt, ...) log_printf("%s: " fmt, __FUNCTION__, __VA_ARGS__)
|
|
|
|
#define FAIL_ON(cond, val) \
|
|
|
|
if (cond) { \
|
|
|
|
log(#cond " (%08X)", val); \
|
|
|
|
return -29; \
|
2018-05-10 13:20:53 +02:00
|
|
|
}
|
|
|
|
|
2018-06-04 11:54:50 +02:00
|
|
|
int _MCP_LoadFile_patch(ipcmessage* msg) {
|
|
|
|
FAIL_ON(!msg->ioctl.buffer_in, 0);
|
|
|
|
FAIL_ON(msg->ioctl.length_in != 0x12D8, msg->ioctl.length_in);
|
|
|
|
FAIL_ON(!msg->ioctl.buffer_io, 0);
|
|
|
|
FAIL_ON(!msg->ioctl.length_io, 0);
|
2018-06-01 13:09:51 +02:00
|
|
|
|
2018-05-10 13:20:53 +02:00
|
|
|
MCPLoadFileRequest* request = (MCPLoadFileRequest*)msg->ioctl.buffer_in;
|
2018-06-04 11:54:50 +02:00
|
|
|
log("msg->ioctl.buffer_io = %p, msg->ioctl.length_io = 0x%X\n", msg->ioctl.buffer_io, msg->ioctl.length_io);
|
|
|
|
log("request->type = %d, request->pos = %d, request->name = \"%s\"\n", request->type, request->pos, request->name);
|
2018-05-10 13:20:53 +02:00
|
|
|
|
2019-08-15 10:29:36 +02:00
|
|
|
/*if (request->type == LOAD_FILE_CAFE_OS &&
|
2018-05-11 08:01:06 +02:00
|
|
|
request->name[0] == '*') {
|
2018-05-10 13:20:53 +02:00
|
|
|
char path[0x40];
|
|
|
|
|
2019-08-15 10:29:36 +02:00
|
|
|
// Translate request->name to a path by replacing * with /
|
2018-05-10 13:20:53 +02:00
|
|
|
for (int i = 0; i < 0x40; ++i) {
|
|
|
|
if (request->name[i] == '*') {
|
|
|
|
path[i] = '/';
|
|
|
|
} else {
|
|
|
|
path[i] = request->name[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-04 11:54:50 +02:00
|
|
|
int result = MCP_LoadCustomFile(path, msg, request);
|
|
|
|
if (result >= 0) return result;
|
2019-08-15 10:29:36 +02:00
|
|
|
}*/
|
2018-06-04 11:54:50 +02:00
|
|
|
/* RPX replacement!
|
2019-08-15 10:29:36 +02:00
|
|
|
|
2018-06-04 11:54:50 +02:00
|
|
|
The goal here is only to replace an rpx once. Reading at pos = 0 signifies a
|
|
|
|
new rpx load - these conditions detect that. */
|
2019-08-15 10:29:36 +02:00
|
|
|
|
|
|
|
if (request->name[0] == 'm' &&
|
|
|
|
request->name[1] == 'e' &&
|
|
|
|
request->name[2] == 'n' &&
|
|
|
|
request->name[3] == '.' &&
|
|
|
|
request->name[4] == 'r' &&
|
|
|
|
request->name[5] == 'p' &&
|
|
|
|
request->name[6] == 'x'
|
|
|
|
&& rpxpath[0] != 'd'
|
|
|
|
&& !skipPPCSetup){
|
|
|
|
int fsa_h = svcOpen("/dev/fsa", 0);
|
|
|
|
FSA_Unmount(fsa_h, "/vol/storage_iosu_homebrew", 2);
|
|
|
|
FSA_Mount(fsa_h, "/dev/sdcard01", "/vol/storage_iosu_homebrew", 2, NULL, 0);
|
|
|
|
svcClose(fsa_h);
|
|
|
|
|
|
|
|
char * f_path = "/vol/storage_iosu_homebrew/wiiu/payload.rpx";;
|
|
|
|
|
|
|
|
int result = MCP_LoadCustomFile(f_path, msg, request);
|
|
|
|
|
|
|
|
if (result >= 0) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}else if (request->name[0] == 's' &&
|
|
|
|
request->name[1] == 'a' &&
|
|
|
|
request->name[2] == 'f' &&
|
|
|
|
request->name[3] == 'e' &&
|
|
|
|
request->name[4] == '.' &&
|
|
|
|
request->name[5] == 'r' &&
|
|
|
|
request->name[6] == 'p' &&
|
|
|
|
request->name[7] == 'x'){
|
|
|
|
|
|
|
|
char * final_path = rpxpath;
|
|
|
|
|
|
|
|
if(rpxpath[0] == '\0') {
|
|
|
|
final_path = "/vol/storage_iosu_homebrew/wiiu/apps/homebrew_launcher/homebrew_launcher.rpx";
|
|
|
|
if (request->pos == 0){
|
|
|
|
didrpxfirstchunk = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
char* extension = request->name + strlen(request->name) - 4;
|
|
|
|
if( extension[0] == '.' &&
|
|
|
|
extension[1] == 'r' &&
|
|
|
|
extension[2] == 'p' &&
|
|
|
|
extension[3] == 'x') {
|
|
|
|
if(final_path != NULL){
|
|
|
|
if (!didrpxfirstchunk || request->pos > 0) {
|
|
|
|
int fsa_h = svcOpen("/dev/fsa", 0);
|
|
|
|
FSA_Unmount(fsa_h, "/vol/storage_iosu_homebrew", 2);
|
|
|
|
FSA_Mount(fsa_h, "/dev/sdcard01", "/vol/storage_iosu_homebrew", 2, NULL, 0);
|
|
|
|
svcClose(fsa_h);
|
|
|
|
|
|
|
|
int result = MCP_LoadCustomFile(final_path, msg, request);
|
|
|
|
|
|
|
|
rpxpath[0] = '\0';
|
|
|
|
if (result >= 0) {
|
|
|
|
if (request->pos == 0) didrpxfirstchunk = true;
|
|
|
|
return result;
|
|
|
|
}
|
2018-06-01 13:09:51 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-05-10 13:20:53 +02:00
|
|
|
}
|
2019-08-15 10:29:36 +02:00
|
|
|
if (rpxpath[0] == 'd' &&
|
|
|
|
rpxpath[1] == 'o' &&
|
|
|
|
rpxpath[2] == 'n' &&
|
|
|
|
rpxpath[3] == 'e'){
|
|
|
|
skipPPCSetup = true;
|
|
|
|
rpxpath[0] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-09 02:34:44 +02:00
|
|
|
return real_MCP_LoadFile(msg);
|
2018-05-08 12:17:40 +02:00
|
|
|
}
|
2018-05-31 13:11:20 +02:00
|
|
|
|
2018-06-04 11:54:50 +02:00
|
|
|
static int MCP_LoadCustomFile(char* path, ipcmessage* msg, MCPLoadFileRequest* request) {
|
|
|
|
log("Load custom path \"%s\"\n", path);
|
2019-08-15 10:29:36 +02:00
|
|
|
|
|
|
|
int filesize = 0;
|
|
|
|
int fileoffset = 0;
|
|
|
|
|
|
|
|
if(filesize > 0 && (request->pos + fileoffset > filesize)){
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: If this fails, try last argument as 1 */
|
2018-06-04 11:54:50 +02:00
|
|
|
int bytesRead = 0;
|
2019-08-15 10:29:36 +02:00
|
|
|
int result = MCP_DoLoadFile(path, NULL, msg->ioctl.buffer_io, msg->ioctl.length_io, request->pos + fileoffset, &bytesRead, 0);
|
|
|
|
log("MCP_DoLoadFile returned %d, bytesRead = %d pos %d \n", result, bytesRead, request->pos + fileoffset);
|
|
|
|
|
2018-06-04 11:54:50 +02:00
|
|
|
|
|
|
|
if (result >= 0) {
|
|
|
|
if (!bytesRead) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: If this fails, try last argument as 1 */
|
2019-08-15 10:29:36 +02:00
|
|
|
result = MCP_UnknownStuff(path, request->pos + fileoffset, msg->ioctl.buffer_io, msg->ioctl.length_io, msg->ioctl.length_io, 0);
|
2018-06-04 11:54:50 +02:00
|
|
|
log("MCP_UnknownStuff returned %d\n", result);
|
2018-06-01 13:09:51 +02:00
|
|
|
|
2018-06-04 11:54:50 +02:00
|
|
|
if (result >= 0) {
|
2019-08-15 10:29:36 +02:00
|
|
|
if(filesize > 0 && (bytesRead + request->pos > filesize)){
|
|
|
|
return filesize - request->pos;
|
|
|
|
}
|
2018-06-04 11:54:50 +02:00
|
|
|
return bytesRead;
|
|
|
|
}
|
2018-06-01 13:09:51 +02:00
|
|
|
}
|
2018-06-04 11:54:50 +02:00
|
|
|
return result;
|
|
|
|
}
|
2018-06-01 13:09:51 +02:00
|
|
|
|
2018-06-04 11:54:50 +02:00
|
|
|
/* RPX replacement! Call this ioctl to replace the next loaded RPX with an arbitrary path.
|
|
|
|
DO NOT RETURN 0, this affects the codepaths back in the IOSU code */
|
|
|
|
int _MCP_ioctl100_patch(ipcmessage* msg) {
|
|
|
|
/* Give some method to detect this ioctl's prescence, even if the other args are bad */
|
|
|
|
if (msg->ioctl.buffer_io && msg->ioctl.length_io >= sizeof(u32)) {
|
|
|
|
*(u32*)msg->ioctl.buffer_io = 1;
|
2018-06-01 13:09:51 +02:00
|
|
|
}
|
|
|
|
|
2018-06-04 11:54:50 +02:00
|
|
|
FAIL_ON(!msg->ioctl.buffer_in, 0);
|
|
|
|
FAIL_ON(!msg->ioctl.length_in, 0);
|
|
|
|
FAIL_ON(msg->ioctl.length_in > sizeof(rpxpath) - 1, msg->ioctl.length_in);
|
2019-08-15 10:29:36 +02:00
|
|
|
|
|
|
|
memset(rpxpath,0,sizeof(rpxpath));
|
|
|
|
strncpy(rpxpath, (const char*)msg->ioctl.buffer_in, msg->ioctl.length_in);
|
|
|
|
//rpxpath[strlen(rpxpath)] = '\0';
|
2018-06-04 11:54:50 +02:00
|
|
|
|
2018-06-01 13:09:51 +02:00
|
|
|
didrpxfirstchunk = false;
|
2018-05-31 13:11:20 +02:00
|
|
|
|
2018-06-04 11:54:50 +02:00
|
|
|
log("Will load %s for next title\n", rpxpath);
|
|
|
|
|
|
|
|
/* Signal that all went well */
|
|
|
|
if (msg->ioctl.buffer_io && msg->ioctl.length_io >= sizeof(u32)) {
|
|
|
|
*(u32*)msg->ioctl.buffer_io = 2;
|
|
|
|
}
|
2018-05-31 13:11:20 +02:00
|
|
|
return 1;
|
|
|
|
}
|