Implement plugin loader
This commit is contained in:
@@ -207,6 +207,21 @@ static Result HBLDR_Init(Handle *session)
|
||||
return res;
|
||||
}
|
||||
|
||||
static Result PLGLDR_Init(Handle *session)
|
||||
{
|
||||
Result res;
|
||||
while (1)
|
||||
{
|
||||
res = svcConnectToPort(session, "plg:ldr");
|
||||
if (R_LEVEL(res) != RL_PERMANENT ||
|
||||
R_SUMMARY(res) != RS_NOTFOUND ||
|
||||
R_DESCRIPTION(res) != RD_NOT_FOUND
|
||||
) break;
|
||||
svcSleepThread(500000);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static Result loader_GetProgramInfo(exheader_header *exheader, u64 prog_handle)
|
||||
{
|
||||
Result res;
|
||||
@@ -365,6 +380,9 @@ static Result loader_LoadProcess(Handle *process, u64 prog_handle)
|
||||
// load code
|
||||
if ((res = load_code(progid, &shared_addr, prog_handle, g_exheader.codesetinfo.flags.flag & 1)) >= 0)
|
||||
{
|
||||
u32 *code = (u32 *)shared_addr.text_addr;
|
||||
bool isHomebrew = code[0] == 0xEA000006 && code[8] == 0xE1A0400E;
|
||||
|
||||
memcpy(&codesetinfo.name, g_exheader.codesetinfo.name, 8);
|
||||
codesetinfo.program_id = progid;
|
||||
codesetinfo.text_addr = vaddr.text_addr;
|
||||
@@ -383,6 +401,33 @@ static Result loader_LoadProcess(Handle *process, u64 prog_handle)
|
||||
svcCloseHandle(codeset);
|
||||
if (res >= 0)
|
||||
{
|
||||
// Try to load a plugin for the game
|
||||
if (!isHomebrew && ((u32)((progid >> 0x20) & 0xFFFFFFEDULL) == 0x00040000))
|
||||
{
|
||||
// Special case handling: games rebooting the 3DS on old models
|
||||
if (!isN3DS && (g_exheader.arm11systemlocalcaps.flags[2] >> 4) > 0)
|
||||
{
|
||||
// Check if the plugin loader is enabled, otherwise skip the loading part
|
||||
s64 out;
|
||||
|
||||
svcGetSystemInfo(&out, 0x10000, 0x102);
|
||||
if ((out & 1) == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
Handle plgldr = 0;
|
||||
|
||||
if (R_SUCCEEDED(PLGLDR_Init(&plgldr)))
|
||||
{
|
||||
u32* cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(1, 0, 2);
|
||||
cmdbuf[1] = IPC_Desc_SharedHandles(1);
|
||||
cmdbuf[2] = *process;
|
||||
svcSendSyncRequest(plgldr);
|
||||
svcCloseHandle(plgldr);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -582,14 +627,14 @@ void __appExit()
|
||||
void __sync_init();
|
||||
void __sync_fini();
|
||||
void __system_initSyscalls();
|
||||
|
||||
|
||||
void __ctru_exit()
|
||||
{
|
||||
__appExit();
|
||||
__sync_fini();
|
||||
svcExitProcess();
|
||||
}
|
||||
|
||||
|
||||
void initSystem()
|
||||
{
|
||||
__sync_init();
|
||||
@@ -662,7 +707,7 @@ int main()
|
||||
g_active_handles--;
|
||||
reply_target = 0;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
|
||||
37
sysmodules/rosalina/include/3gx.h
Normal file
37
sysmodules/rosalina/include/3gx.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include <3ds/types.h>
|
||||
#include "ifile.h"
|
||||
|
||||
#define _3GX_MAGIC (0x3130303024584733) /* "3GX$0001" */
|
||||
|
||||
typedef struct PACKED
|
||||
{
|
||||
u32 authorLen;
|
||||
const char * authorMsg;
|
||||
u32 titleLen;
|
||||
const char * titleMsg;
|
||||
u32 summaryLen;
|
||||
const char * summaryMsg;
|
||||
u32 descriptionLen;
|
||||
const char * descriptionMsg;
|
||||
} _3gx_Infos;
|
||||
|
||||
typedef struct PACKED
|
||||
{
|
||||
u32 count;
|
||||
u32 * titles;
|
||||
} _3gx_Targets;
|
||||
|
||||
typedef struct PACKED
|
||||
{
|
||||
u64 magic;
|
||||
u32 version;
|
||||
u32 codeSize;
|
||||
u32 * code;
|
||||
_3gx_Infos infos;
|
||||
_3gx_Targets targets;
|
||||
} _3gx_Header;
|
||||
|
||||
|
||||
Result Read_3gx_Header(IFile *file, _3gx_Header *header);
|
||||
Result Read_3gx_Code(IFile *file, _3gx_Header *header, void *dst);
|
||||
@@ -71,18 +71,20 @@ void svcInvalidateEntireInstructionCache(void);
|
||||
///@{
|
||||
/**
|
||||
* @brief Maps a block of process memory.
|
||||
* @param process Handle of the process.
|
||||
* @param dstProcessHandle Handle of the process to map the memory in (destination)
|
||||
* @param destAddress Address of the mapped block in the current process.
|
||||
* @param srcProcessHandle Handle of the process to map the memory from (source)
|
||||
* @param srcAddress Address of the mapped block in the source process.
|
||||
* @param size Size of the block of the memory to map (truncated to a multiple of 0x1000 bytes).
|
||||
*/
|
||||
Result svcMapProcessMemoryEx(Handle process, u32 destAddr, u32 srcAddr, u32 size);
|
||||
Result svcMapProcessMemoryEx(Handle dstProcessHandle, u32 destAddress, Handle srcProcessHandle, u32 vaSrc, u32 size);
|
||||
|
||||
/**
|
||||
* @brief Unmaps a block of process memory.
|
||||
* @param process Handle of the process.
|
||||
* @param destAddress Address of the block of memory to unmap, in the current (destination) process.
|
||||
* @param process Handle of the process to unmap the memory from
|
||||
* @param destAddress Address of the block of memory to unmap
|
||||
* @param size Size of the block of memory to unmap (truncated to a multiple of 0x1000 bytes).
|
||||
* This function should only be used to unmap memory mapped with svcMapProcessMemoryEx
|
||||
*/
|
||||
Result svcUnmapProcessMemoryEx(Handle process, u32 destAddress, u32 size);
|
||||
|
||||
@@ -134,4 +136,19 @@ Result svcCopyHandle(Handle *out, Handle outProcess, Handle in, Handle inProcess
|
||||
* @param in The input handle.
|
||||
*/
|
||||
Result svcTranslateHandle(u32 *outKAddr, char *outClassName, Handle in);
|
||||
|
||||
/// Operations for svcControlProcess
|
||||
typedef enum ProcessOp
|
||||
{
|
||||
PROCESSOP_GET_ALL_HANDLES, ///< List all handles of the process, varg3 can be either 0 to fetch all handles, or token of the type to fetch
|
||||
///< svcControlProcess(handle, PROCESSOP_GET_ALL_HANDLES, (u32)&outBuf, 0)
|
||||
PROCESSOP_SET_MMU_TO_RWX, ///< Set the whole memory of the process with rwx access
|
||||
///< svcControlProcess(handle, PROCESSOP_SET_MMU_TO_RWX, 0, 0)
|
||||
PROCESSOP_GET_ON_MEMORY_CHANGE_EVENT,
|
||||
PROCESSOP_GET_ON_EXIT_EVENT,
|
||||
PROCESSOP_GET_PA_FROM_VA, ///< Get the physical address of the va within the process
|
||||
///< svcControlProcess(handle, PROCESSOP_GET_PA_FROM_VA, (u32)&outPa, va)
|
||||
} ProcessOp;
|
||||
|
||||
Result svcControlProcess(Handle process, ProcessOp op, u32 varg2, u32 varg3);
|
||||
///@}
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
#define COLOR_WHITE RGB565(0x1F, 0x3F, 0x1F)
|
||||
#define COLOR_RED RGB565(0x1F, 0x00, 0x00)
|
||||
#define COLOR_GREEN RGB565(0x00, 0x1F, 0x00)
|
||||
#define COLOR_LIME RGB565(0x00, 0xFF, 0x00)
|
||||
#define COLOR_BLACK RGB565(0x00, 0x00, 0x00)
|
||||
|
||||
#define DRAW_MAX_FORMATTED_STRING_SIZE 512
|
||||
|
||||
@@ -31,8 +31,10 @@
|
||||
#define GDB_REMOTE_COMMAND_HANDLER(name) GDB_HANDLER(RemoteCommand##name)
|
||||
#define GDB_DECLARE_REMOTE_COMMAND_HANDLER(name) GDB_DECLARE_HANDLER(RemoteCommand##name)
|
||||
|
||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ConvertVAToPA);
|
||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(SyncRequestInfo);
|
||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle);
|
||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ListAllHandles);
|
||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig);
|
||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMemRegions);
|
||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(FlushCaches);
|
||||
|
||||
@@ -35,6 +35,7 @@ void *memset32(void *dest, u32 value, u32 size);
|
||||
u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize);
|
||||
char *strcpy(char *dest, const char *src);
|
||||
char *strncpy(char *dest, const char *src, u32 size);
|
||||
char *strcat(char *dest, const char *src);
|
||||
s32 strnlen(const char *string, s32 maxlen);
|
||||
s32 strlen(const char *string);
|
||||
s32 strcmp(const char *str1, const char *str2);
|
||||
|
||||
@@ -75,14 +75,17 @@ extern Handle terminationRequestEvent;
|
||||
|
||||
extern u32 menuCombo;
|
||||
|
||||
u32 waitInputWithTimeout(u32 msec);
|
||||
u32 waitInput(void);
|
||||
u32 waitInputWithTimeout(u32 msec);
|
||||
u32 waitInput(void);
|
||||
|
||||
u32 waitComboWithTimeout(u32 msec);
|
||||
u32 waitCombo(void);
|
||||
u32 waitComboWithTimeout(u32 msec);
|
||||
u32 waitCombo(void);
|
||||
|
||||
MyThread *menuCreateThread(void);
|
||||
void menuEnter(void);
|
||||
void menuLeave(void);
|
||||
void menuThreadMain(void);
|
||||
void menuShow(Menu *root);
|
||||
void menuEnter(void);
|
||||
void menuLeave(void);
|
||||
void menuThreadMain(void);
|
||||
void menuShow(Menu *root);
|
||||
void DispMessage(const char *title, const char *message);
|
||||
u32 DispErrMessage(const char *title, const char *message, const Result error);
|
||||
void DisplayPluginMenu(u32 *cmdbuf);
|
||||
|
||||
32
sysmodules/rosalina/include/plgldr.h
Normal file
32
sysmodules/rosalina/include/plgldr.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
#define MAX_BUFFER (50)
|
||||
#define MAX_ITEMS_COUNT (64)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool noFlash;
|
||||
u32 lowTitleId;
|
||||
char path[256];
|
||||
u32 config[32];
|
||||
} PluginLoadParameters;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 nbItems;
|
||||
u8 states[MAX_ITEMS_COUNT];
|
||||
char title[MAX_BUFFER];
|
||||
char items[MAX_ITEMS_COUNT][MAX_BUFFER];
|
||||
char hints[MAX_ITEMS_COUNT][MAX_BUFFER];
|
||||
} PluginMenu;
|
||||
|
||||
Result plgLdrInit(void);
|
||||
void plgLdrExit(void);
|
||||
Result PLGLDR__IsPluginLoaderEnabled(bool *isEnabled);
|
||||
Result PLGLDR__SetPluginLoaderState(bool enabled);
|
||||
Result PLGLDR__SetPluginLoadParameters(PluginLoadParameters *parameters);
|
||||
Result PLGLDR__DisplayMenu(PluginMenu *menu);
|
||||
Result PLGLDR__DisplayMessage(const char *title, const char *body);
|
||||
Result PLGLDR__DisplayErrMessage(const char *title, const char *body, u32 error);
|
||||
9
sysmodules/rosalina/include/plgloader.h
Normal file
9
sysmodules/rosalina/include/plgloader.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
#include "MyThread.h"
|
||||
|
||||
MyThread * PluginLoader__CreateThread(void);
|
||||
bool PluginLoader__IsEnabled(void);
|
||||
void PluginLoader__MenuCallback(void);
|
||||
void PluginLoader__UpdateMenu(void);
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include <3ds/svc.h>
|
||||
#include <3ds/result.h>
|
||||
#include <3ds/ipc.h>
|
||||
#include "csvc.h"
|
||||
|
||||
// For accessing physmem uncached (and directly)
|
||||
@@ -56,4 +57,19 @@ static inline void *decodeARMBranch(const void *src)
|
||||
return (void *)((const u8 *)src + 8 + off);
|
||||
}
|
||||
|
||||
static inline void assertSuccess(Result res)
|
||||
{
|
||||
if(R_FAILED(res))
|
||||
svcBreak(USERBREAK_PANIC);
|
||||
}
|
||||
|
||||
static inline void error(u32* cmdbuf, Result rc)
|
||||
{
|
||||
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
|
||||
cmdbuf[1] = rc;
|
||||
}
|
||||
|
||||
extern bool isN3DS;
|
||||
|
||||
Result OpenProcessByName(const char *name, Handle *h);
|
||||
Result SaveSettings(void);
|
||||
|
||||
62
sysmodules/rosalina/source/3gx.c
Normal file
62
sysmodules/rosalina/source/3gx.c
Normal file
@@ -0,0 +1,62 @@
|
||||
#include <3ds.h>
|
||||
#include "3gx.h"
|
||||
|
||||
Result Read_3gx_Header(IFile *file, _3gx_Header *header)
|
||||
{
|
||||
u64 total;
|
||||
char * dst;
|
||||
Result res = 0;
|
||||
|
||||
res = IFile_Read(file, &total, header, sizeof(_3gx_Header));
|
||||
if (R_FAILED(res))
|
||||
return res;
|
||||
|
||||
// Read author
|
||||
file->pos = (u32)header->infos.authorMsg;
|
||||
dst = (char *)header + sizeof(_3gx_Header);
|
||||
res = IFile_Read(file, &total, dst, header->infos.authorLen);
|
||||
if (R_FAILED(res))
|
||||
return res;
|
||||
|
||||
// Relocate ptr
|
||||
header->infos.authorMsg = dst;
|
||||
|
||||
// Read title
|
||||
file->pos = (u32)header->infos.titleMsg;
|
||||
dst += header->infos.authorLen;
|
||||
res = IFile_Read(file, &total, dst, header->infos.titleLen);
|
||||
if (R_FAILED(res))
|
||||
return res;
|
||||
|
||||
// Relocate ptr
|
||||
header->infos.titleMsg = dst;
|
||||
|
||||
// Declare other members as null (unused in our case)
|
||||
header->infos.summaryLen = 0;
|
||||
header->infos.summaryMsg = NULL;
|
||||
header->infos.descriptionLen = 0;
|
||||
header->infos.descriptionMsg = NULL;
|
||||
|
||||
// Read targets compatibility
|
||||
file->pos = (u32)header->targets.titles;
|
||||
dst += header->infos.titleLen;
|
||||
dst += 4 - ((u32)dst & 3); // 4 bytes aligned
|
||||
res = IFile_Read(file, &total, dst, header->targets.count * sizeof(u32));
|
||||
if (R_FAILED(res))
|
||||
return res;
|
||||
|
||||
// Relocate ptr
|
||||
header->targets.titles = (u32 *)dst;
|
||||
return res;
|
||||
}
|
||||
|
||||
Result Read_3gx_Code(IFile *file, _3gx_Header *header, void *dst)
|
||||
{
|
||||
u64 total;
|
||||
Result res = 0;
|
||||
|
||||
file->pos = (u32)header->code;
|
||||
res = IFile_Read(file, &total, dst, header->codeSize);
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -59,7 +59,10 @@ SVC_BEGIN svcInvalidateEntireInstructionCache
|
||||
SVC_END
|
||||
|
||||
SVC_BEGIN svcMapProcessMemoryEx
|
||||
str r4, [sp, #-4]!
|
||||
ldr r4, [sp, #4]
|
||||
svc 0xA0
|
||||
ldr r4, [sp], #4
|
||||
bx lr
|
||||
SVC_END
|
||||
|
||||
@@ -99,3 +102,8 @@ SVC_BEGIN svcTranslateHandle
|
||||
str r1, [r2]
|
||||
bx lr
|
||||
SVC_END
|
||||
|
||||
SVC_BEGIN svcControlProcess
|
||||
svc 0xB3
|
||||
bx lr
|
||||
SVC_END
|
||||
|
||||
@@ -32,12 +32,6 @@
|
||||
#include "fmt.h"
|
||||
#include "ifile.h"
|
||||
|
||||
static inline void assertSuccess(Result res)
|
||||
{
|
||||
if(R_FAILED(res))
|
||||
svcBreak(USERBREAK_PANIC);
|
||||
}
|
||||
|
||||
static MyThread errDispThread;
|
||||
static u8 ALIGN(8) errDispThreadStack[0x2000];
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "csvc.h"
|
||||
#include "fmt.h"
|
||||
#include "gdb/breakpoints.h"
|
||||
#include "utils.h"
|
||||
|
||||
struct
|
||||
{
|
||||
@@ -36,8 +37,10 @@ struct
|
||||
GDBCommandHandler handler;
|
||||
} remoteCommandHandlers[] =
|
||||
{
|
||||
{ "convertvatopa" , GDB_REMOTE_COMMAND_HANDLER(ConvertVAToPA) },
|
||||
{ "syncrequestinfo" , GDB_REMOTE_COMMAND_HANDLER(SyncRequestInfo) },
|
||||
{ "translatehandle" , GDB_REMOTE_COMMAND_HANDLER(TranslateHandle) },
|
||||
{ "listallhandles" , GDB_REMOTE_COMMAND_HANDLER(ListAllHandles) },
|
||||
{ "getmmuconfig" , GDB_REMOTE_COMMAND_HANDLER(GetMmuConfig) },
|
||||
{ "getmemregions" , GDB_REMOTE_COMMAND_HANDLER(GetMemRegions) },
|
||||
{ "flushcaches" , GDB_REMOTE_COMMAND_HANDLER(FlushCaches) },
|
||||
@@ -51,6 +54,50 @@ static const char *GDB_SkipSpaces(const char *pos)
|
||||
return nextpos;
|
||||
}
|
||||
|
||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ConvertVAToPA)
|
||||
{
|
||||
bool ok;
|
||||
int n;
|
||||
u32 val;
|
||||
u32 pa;
|
||||
char * end;
|
||||
char outbuf[GDB_BUF_LEN / 2 + 1];
|
||||
|
||||
if(ctx->commandData[0] == 0)
|
||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
||||
|
||||
val = xstrtoul(ctx->commandData, &end, 0, true, &ok);
|
||||
|
||||
if(!ok)
|
||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
||||
|
||||
if (val >= 0x40000000)
|
||||
pa = svcConvertVAToPA((const void *)val, false);
|
||||
else
|
||||
{
|
||||
Handle process;
|
||||
Result r = svcOpenProcess(&process, ctx->pid);
|
||||
if(R_FAILED(r))
|
||||
{
|
||||
n = sprintf(outbuf, "Invalid process (wtf?)\n");
|
||||
goto end;
|
||||
}
|
||||
r = svcControlProcess(process, PROCESSOP_GET_PA_FROM_VA, (u32)&pa, val);
|
||||
svcCloseHandle(process);
|
||||
|
||||
if (R_FAILED(r))
|
||||
{
|
||||
n = sprintf(outbuf, "An error occured: %08X\n", r);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
n = sprintf(outbuf, "va: 0x%08X, pa: 0x%08X, b31: 0x%08X\n", val, pa, pa | (1 << 31));
|
||||
end:
|
||||
return GDB_SendHexPacket(ctx, outbuf, n);
|
||||
}
|
||||
|
||||
|
||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(SyncRequestInfo)
|
||||
{
|
||||
char outbuf[GDB_BUF_LEN / 2 + 1];
|
||||
@@ -135,6 +182,29 @@ end:
|
||||
return GDB_SendHexPacket(ctx, outbuf, n);
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
TOKEN_KAUTOOBJECT = 0,
|
||||
TOKEN_KSYNCHRONIZATIONOBJECT = 1,
|
||||
TOKEN_KEVENT = 0x1F,
|
||||
TOKEN_KSEMAPHORE = 0x2F,
|
||||
TOKEN_KTIMER = 0x35,
|
||||
TOKEN_KMUTEX = 0x39,
|
||||
TOKEN_KDEBUG = 0x4D,
|
||||
TOKEN_KSERVERPORT = 0x55,
|
||||
TOKEN_KDMAOBJECT = 0x59,
|
||||
TOKEN_KCLIENTPORT = 0x65,
|
||||
TOKEN_KCODESET = 0x68,
|
||||
TOKEN_KSESSION = 0x70,
|
||||
TOKEN_KTHREAD = 0x8D,
|
||||
TOKEN_KSERVERSESSION = 0x95,
|
||||
TOKEN_KCLIENTSESSION = 0xA5,
|
||||
TOKEN_KPORT = 0xA8,
|
||||
TOKEN_KSHAREDMEMORY = 0xB0,
|
||||
TOKEN_KPROCESS = 0xC5,
|
||||
TOKEN_KRESOURCELIMIT = 0xC8
|
||||
};
|
||||
|
||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
|
||||
{
|
||||
bool ok;
|
||||
@@ -143,10 +213,11 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
|
||||
int n;
|
||||
Result r;
|
||||
u32 kernelAddr;
|
||||
s64 token;
|
||||
Handle handle, process;
|
||||
s64 refcountRaw;
|
||||
u32 refcount;
|
||||
char classBuf[32], serviceBuf[12] = { 0 };
|
||||
char classBuf[32], serviceBuf[12] = {0}, ownerBuf[50] = { 0 };
|
||||
char outbuf[GDB_BUF_LEN / 2 + 1];
|
||||
|
||||
if(ctx->commandData[0] == 0)
|
||||
@@ -178,12 +249,29 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
|
||||
|
||||
svcTranslateHandle(&kernelAddr, classBuf, handle);
|
||||
svcGetHandleInfo(&refcountRaw, handle, 1);
|
||||
svcGetHandleInfo(&token, handle, 0x10001);
|
||||
svcControlService(SERVICEOP_GET_NAME, serviceBuf, handle);
|
||||
refcount = (u32)(refcountRaw - 1);
|
||||
|
||||
if(serviceBuf[0] != 0)
|
||||
n = sprintf(outbuf, "(%s *)0x%08x /* %s handle, %u %s */\n", classBuf, kernelAddr, serviceBuf, refcount, refcount == 1 ? "reference" : "references");
|
||||
else if (token == TOKEN_KPROCESS)
|
||||
{
|
||||
svcGetProcessInfo((s64 *)serviceBuf, handle, 0x10000);
|
||||
n = sprintf(outbuf, "(%s *)0x%08x /* process: %s, %u %s */\n", classBuf, kernelAddr, serviceBuf, refcount, refcount == 1 ? "reference" : "references");
|
||||
}
|
||||
else
|
||||
n = sprintf(outbuf, "(%s *)0x%08x /* %u %s */\n", classBuf, kernelAddr, refcount, refcount == 1 ? "reference" : "references");
|
||||
{
|
||||
s64 owner;
|
||||
|
||||
if (R_SUCCEEDED(svcGetHandleInfo(&owner, handle, 0x10002)))
|
||||
{
|
||||
svcGetProcessInfo((s64 *)serviceBuf, (u32)owner, 0x10000);
|
||||
svcCloseHandle((u32)owner);
|
||||
sprintf(ownerBuf, " owner: %s", serviceBuf);
|
||||
}
|
||||
n = sprintf(outbuf, "(%s *)0x%08x /* %u %s%s */\n", classBuf, kernelAddr, refcount, refcount == 1 ? "reference" : "references", ownerBuf);
|
||||
}
|
||||
|
||||
end:
|
||||
svcCloseHandle(handle);
|
||||
@@ -191,6 +279,68 @@ end:
|
||||
return GDB_SendHexPacket(ctx, outbuf, n);
|
||||
}
|
||||
|
||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ListAllHandles)
|
||||
{
|
||||
bool ok;
|
||||
u32 val;
|
||||
char *end;
|
||||
int n = 0;
|
||||
Result r;
|
||||
s32 count = 0;
|
||||
Handle process, procHandles[0x100];
|
||||
char outbuf[GDB_BUF_LEN / 2 + 1];
|
||||
|
||||
if(ctx->commandData[0] == 0)
|
||||
val = 0; ///< All handles
|
||||
else
|
||||
{ // Get handles of specified type
|
||||
val = xstrtoul(ctx->commandData, &end, 0, true, &ok);
|
||||
|
||||
if(!ok)
|
||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
||||
|
||||
end = (char *)GDB_SkipSpaces(end);
|
||||
|
||||
if(*end != 0)
|
||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
||||
}
|
||||
|
||||
r = svcOpenProcess(&process, ctx->pid);
|
||||
if(R_FAILED(r))
|
||||
{
|
||||
n = sprintf(outbuf, "Invalid process (wtf?)\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (R_FAILED(count = svcControlProcess(process, PROCESSOP_GET_ALL_HANDLES, (u32)procHandles, val)))
|
||||
n = sprintf(outbuf, "An error occured: %08X\n", count);
|
||||
else if (count == 0)
|
||||
n = sprintf(outbuf, "Process has no handles ?\n");
|
||||
else
|
||||
{
|
||||
n = sprintf(outbuf, "Found %d handles.\n", count);
|
||||
|
||||
const char *comma = "";
|
||||
for (s32 i = 0; i < count && n < (GDB_BUF_LEN >> 1) - 20; ++i)
|
||||
{
|
||||
Handle handle = procHandles[i];
|
||||
|
||||
n += sprintf(outbuf + n, "%s0x%08X", comma, handle);
|
||||
|
||||
if (((i + 1) % 8) == 0)
|
||||
{
|
||||
outbuf[n++] = '\n';
|
||||
comma = "";
|
||||
}
|
||||
else
|
||||
comma = ", ";
|
||||
}
|
||||
}
|
||||
end:
|
||||
svcCloseHandle(process);
|
||||
return GDB_SendHexPacket(ctx, outbuf, n);
|
||||
}
|
||||
|
||||
extern bool isN3DS;
|
||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig)
|
||||
{
|
||||
|
||||
@@ -117,12 +117,6 @@ static const u32 kernelCaps[] =
|
||||
0xFE000200, // Handle table size: 0x200
|
||||
};
|
||||
|
||||
static inline void assertSuccess(Result res)
|
||||
{
|
||||
if(R_FAILED(res))
|
||||
svcBreak(USERBREAK_PANIC);
|
||||
}
|
||||
|
||||
static MyThread hbldrThread;
|
||||
static u8 ALIGN(8) hbldrThreadStack[THREAD_STACK_SIZE];
|
||||
static u16 hbldrTarget[PATH_MAX+1];
|
||||
@@ -133,12 +127,6 @@ MyThread *hbldrCreateThread(void)
|
||||
return &hbldrThread;
|
||||
}
|
||||
|
||||
static inline void error(u32* cmdbuf, Result rc)
|
||||
{
|
||||
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
|
||||
cmdbuf[1] = rc;
|
||||
}
|
||||
|
||||
static u16 *u16_strncpy(u16 *dest, const u16 *src, u32 size)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
@@ -166,7 +166,7 @@ Result InputRedirection_DoOrUndoPatches(void)
|
||||
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
|
||||
|
||||
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
|
||||
res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, totalSize);
|
||||
res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, processHandle, (u32) startAddress, totalSize);
|
||||
|
||||
if(R_SUCCEEDED(res))
|
||||
{
|
||||
@@ -203,21 +203,21 @@ Result InputRedirection_DoOrUndoPatches(void)
|
||||
u32 *off = (u32 *)memsearch((u8 *)0x00100000, &hidOrigRegisterAndValue, totalSize, sizeof(hidOrigRegisterAndValue));
|
||||
if(off == NULL)
|
||||
{
|
||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
u32 *off2 = (u32 *)memsearch((u8 *)off + sizeof(hidOrigRegisterAndValue), &hidOrigRegisterAndValue, totalSize - ((u32)off - 0x00100000), sizeof(hidOrigRegisterAndValue));
|
||||
if(off2 == NULL)
|
||||
{
|
||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
||||
return -2;
|
||||
}
|
||||
|
||||
u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &hidOrigCode, totalSize, sizeof(hidOrigCode));
|
||||
if(off3 == NULL)
|
||||
{
|
||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
||||
return -3;
|
||||
}
|
||||
|
||||
@@ -230,7 +230,7 @@ Result InputRedirection_DoOrUndoPatches(void)
|
||||
}
|
||||
}
|
||||
|
||||
res = svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||
res = svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
||||
}
|
||||
svcCloseHandle(processHandle);
|
||||
|
||||
@@ -244,7 +244,7 @@ Result InputRedirection_DoOrUndoPatches(void)
|
||||
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
|
||||
|
||||
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
|
||||
res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, totalSize);
|
||||
res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, processHandle, (u32) startAddress, totalSize);
|
||||
|
||||
if(R_SUCCEEDED(res))
|
||||
{
|
||||
@@ -298,7 +298,7 @@ Result InputRedirection_DoOrUndoPatches(void)
|
||||
u32 *off = (u32 *)memsearch((u8 *)0x00100000, &irOrigReadingCode, totalSize, sizeof(irOrigReadingCode) - 4);
|
||||
if(off == NULL)
|
||||
{
|
||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
||||
return -4;
|
||||
}
|
||||
|
||||
@@ -308,7 +308,7 @@ Result InputRedirection_DoOrUndoPatches(void)
|
||||
off2 = (u32 *)memsearch((u8 *)0x00100000, &irOrigWaitSyncCodeOld, totalSize, sizeof(irOrigWaitSyncCodeOld));
|
||||
if(off2 == NULL)
|
||||
{
|
||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
||||
return -5;
|
||||
}
|
||||
|
||||
@@ -320,7 +320,7 @@ Result InputRedirection_DoOrUndoPatches(void)
|
||||
u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &irOrigCppFlagCode, totalSize, sizeof(irOrigCppFlagCode));
|
||||
if(off3 == NULL)
|
||||
{
|
||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
||||
return -6;
|
||||
}
|
||||
|
||||
@@ -343,7 +343,7 @@ Result InputRedirection_DoOrUndoPatches(void)
|
||||
}
|
||||
}
|
||||
|
||||
res = svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
||||
res = svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
||||
}
|
||||
svcCloseHandle(processHandle);
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "MyThread.h"
|
||||
#include "menus/process_patches.h"
|
||||
#include "menus/miscellaneous.h"
|
||||
#include "plgloader.h"
|
||||
|
||||
// this is called before main
|
||||
bool isN3DS;
|
||||
@@ -111,7 +112,8 @@ int main(void)
|
||||
Result res = 0;
|
||||
Handle notificationHandle;
|
||||
|
||||
MyThread *menuThread = menuCreateThread(), *errDispThread = errDispCreateThread(), *hbldrThread = hbldrCreateThread();
|
||||
MyThread *menuThread = menuCreateThread(), *errDispThread = errDispCreateThread(),
|
||||
*hbldrThread = hbldrCreateThread(), *plgloaderThread = PluginLoader__CreateThread();
|
||||
|
||||
if(R_FAILED(srvEnableNotification(¬ificationHandle)))
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
@@ -147,6 +149,7 @@ int main(void)
|
||||
MyThread_Join(menuThread, -1LL);
|
||||
MyThread_Join(errDispThread, -1LL);
|
||||
MyThread_Join(hbldrThread, -1LL);
|
||||
MyThread_Join(plgloaderThread, -1LL);
|
||||
|
||||
svcCloseHandle(notificationHandle);
|
||||
return 0;
|
||||
|
||||
@@ -118,6 +118,18 @@ char *strncpy(char *dest, const char *src, u32 size)
|
||||
return dest;
|
||||
}
|
||||
|
||||
char *strcat(char *dest, const char *src)
|
||||
{
|
||||
char *dst = dest;
|
||||
|
||||
while(*dest) ++dest;
|
||||
while (*src)
|
||||
*dest++ = *src++;
|
||||
*dest = 0;
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
s32 strnlen(const char *string, s32 maxlen)
|
||||
{
|
||||
s32 size;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "ifile.h"
|
||||
#include "menus.h"
|
||||
#include "utils.h"
|
||||
#include "plgloader.h"
|
||||
#include "menus/n3ds.h"
|
||||
#include "menus/cheats.h"
|
||||
#include "minisoc.h"
|
||||
@@ -127,9 +128,11 @@ u32 waitCombo(void)
|
||||
static MyThread menuThread;
|
||||
static u8 ALIGN(8) menuThreadStack[THREAD_STACK_SIZE];
|
||||
static u8 batteryLevel = 255;
|
||||
static u32 homeBtnPressed = 0;
|
||||
|
||||
MyThread *menuCreateThread(void)
|
||||
{
|
||||
svcKernelSetState(0x10007, &homeBtnPressed);
|
||||
if(R_FAILED(MyThread_Create(&menuThread, menuThreadMain, menuThreadStack, THREAD_STACK_SIZE, 52, CORE_SYSTEM)))
|
||||
svcBreak(USERBREAK_PANIC);
|
||||
return &menuThread;
|
||||
@@ -138,7 +141,9 @@ MyThread *menuCreateThread(void)
|
||||
extern bool isN3DS;
|
||||
u32 menuCombo;
|
||||
|
||||
void menuThreadMain(void)
|
||||
u32 DispWarningOnHome(void);
|
||||
|
||||
void menuThreadMain(void)
|
||||
{
|
||||
if(!isN3DS)
|
||||
{
|
||||
@@ -159,10 +164,11 @@ void menuThreadMain(void)
|
||||
isAcURegistered = R_SUCCEEDED(srvIsServiceRegistered(&isAcURegistered, "ac:u"))
|
||||
&& isAcURegistered;
|
||||
|
||||
if (isAcURegistered)
|
||||
else
|
||||
{
|
||||
menuEnter();
|
||||
if(isN3DS) N3DSMenu_UpdateStatus();
|
||||
PluginLoader__UpdateMenu();
|
||||
menuShow(&rosalinaMenu);
|
||||
menuLeave();
|
||||
}
|
||||
@@ -173,6 +179,16 @@ void menuThreadMain(void)
|
||||
Cheat_ApplyKeyCheats();
|
||||
}
|
||||
}
|
||||
|
||||
// Check for home button on O3DS Mode3 with plugin loaded
|
||||
if (homeBtnPressed != 0)
|
||||
{
|
||||
if (DispWarningOnHome())
|
||||
svcKernelSetState(7); ///< reboot is fine since exiting a mode3 game reboot anyway
|
||||
|
||||
homeBtnPressed = 0;
|
||||
}
|
||||
|
||||
svcSleepThread(50 * 1000 * 1000LL);
|
||||
}
|
||||
}
|
||||
@@ -344,3 +360,158 @@ void menuShow(Menu *root)
|
||||
}
|
||||
while(!terminationRequest);
|
||||
}
|
||||
|
||||
static const char *__press_b_to_close = "Press [B] to close";
|
||||
|
||||
void DispMessage(const char *title, const char *message)
|
||||
{
|
||||
menuEnter();
|
||||
|
||||
Draw_Lock();
|
||||
Draw_ClearFramebuffer();
|
||||
Draw_FlushFramebuffer();
|
||||
|
||||
Draw_DrawString(10, 10, COLOR_TITLE, title);
|
||||
|
||||
Draw_DrawString(30, 30, COLOR_WHITE, message);
|
||||
Draw_DrawString(200, 220, COLOR_TITLE, __press_b_to_close);
|
||||
|
||||
|
||||
u32 keys = 0;
|
||||
|
||||
do
|
||||
{
|
||||
keys = waitComboWithTimeout(1000);
|
||||
}while (!terminationRequest && !(keys & BUTTON_B));
|
||||
|
||||
Draw_Unlock(); ///< Keep it locked until we exit the message
|
||||
menuLeave();
|
||||
}
|
||||
|
||||
u32 DispErrMessage(const char *title, const char *message, const Result error)
|
||||
{
|
||||
char buf[100];
|
||||
|
||||
sprintf(buf, "Error code: 0x%08X", error);
|
||||
menuEnter();
|
||||
|
||||
Draw_Lock();
|
||||
Draw_ClearFramebuffer();
|
||||
Draw_FlushFramebuffer();
|
||||
|
||||
Draw_DrawString(10, 10, COLOR_TITLE, title);
|
||||
|
||||
u32 posY = Draw_DrawString(30, 30, COLOR_WHITE, message);
|
||||
Draw_DrawString(30, posY + 20, COLOR_RED, buf);
|
||||
Draw_DrawString(200, 220, COLOR_TITLE, __press_b_to_close);
|
||||
|
||||
u32 keys = 0;
|
||||
|
||||
do
|
||||
{
|
||||
keys = waitComboWithTimeout(1000);
|
||||
}while (!terminationRequest && !(keys & BUTTON_B));
|
||||
|
||||
Draw_Unlock(); ///< Keep it locked until we exit the message
|
||||
menuLeave();
|
||||
return error;
|
||||
}
|
||||
|
||||
u32 DispWarningOnHome(void)
|
||||
{
|
||||
menuEnter();
|
||||
|
||||
Draw_Lock();
|
||||
Draw_ClearFramebuffer();
|
||||
Draw_FlushFramebuffer();
|
||||
|
||||
Draw_DrawString(10, 10, COLOR_TITLE, "Warning");
|
||||
|
||||
u32 posY = Draw_DrawString(30, 40, COLOR_WHITE, "Due to memory shortage the home button\nis disabled.");
|
||||
Draw_DrawString(30, posY + 20, COLOR_WHITE, "Press [DPAD UP + B] to exit the application.");
|
||||
Draw_DrawString(200, 220, COLOR_TITLE, __press_b_to_close);
|
||||
|
||||
|
||||
u32 keys = 0;
|
||||
|
||||
do
|
||||
{
|
||||
keys = waitComboWithTimeout(1000);
|
||||
}while (!terminationRequest && !(keys & BUTTON_B));
|
||||
|
||||
Draw_Unlock(); ///< Keep it locked until we exit the message
|
||||
menuLeave();
|
||||
|
||||
return (keys & BUTTON_UP) > 0;
|
||||
}
|
||||
|
||||
|
||||
typedef char string[50];
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
void DisplayPluginMenu(u32 *cmdbuf)
|
||||
{
|
||||
u32 cursor = 0;
|
||||
u32 nbItems = cmdbuf[1];
|
||||
u8 *states = (u8 *)cmdbuf[3];
|
||||
char buffer[60];
|
||||
const char *title = (const char *)cmdbuf[5];
|
||||
const string *items = (const string *)cmdbuf[7];
|
||||
const string *hints = (const string *)cmdbuf[9];
|
||||
|
||||
menuEnter();
|
||||
Draw_Lock();
|
||||
|
||||
do
|
||||
{
|
||||
// Draw the menu
|
||||
{
|
||||
// Clear screen
|
||||
Draw_ClearFramebuffer();
|
||||
Draw_FlushFramebuffer();
|
||||
|
||||
// Draw title
|
||||
Draw_DrawString(10, 10, COLOR_TITLE, title);
|
||||
|
||||
// Draw items
|
||||
u32 i = MAX(0, (int)cursor - 7);
|
||||
u32 end = MIN(nbItems, i + 16);
|
||||
u32 posY = 30;
|
||||
|
||||
for (; i < end; ++i, posY += 10)
|
||||
{
|
||||
sprintf(buffer, "[ ] %s", items[i]);
|
||||
Draw_DrawString(30, posY, COLOR_WHITE, buffer);
|
||||
|
||||
if (i == cursor) Draw_DrawCharacter(10, posY, COLOR_TITLE, '>');
|
||||
if (states[i]) Draw_DrawCharacter(36, posY, COLOR_LIME, 'x');
|
||||
}
|
||||
|
||||
// Draw hint
|
||||
if (hints[cursor])
|
||||
Draw_DrawString(10, 200, COLOR_TITLE, hints[cursor]);
|
||||
}
|
||||
|
||||
// Wait for input
|
||||
u32 pressed = waitInput();
|
||||
|
||||
if (pressed & BUTTON_A)
|
||||
states[cursor] = !states[cursor];
|
||||
|
||||
if (pressed & BUTTON_B)
|
||||
break;
|
||||
|
||||
if (pressed & BUTTON_DOWN)
|
||||
if (++cursor >= nbItems)
|
||||
cursor = 0;
|
||||
|
||||
if (pressed & BUTTON_UP)
|
||||
if (--cursor >= nbItems)
|
||||
cursor = nbItems - 1;
|
||||
|
||||
} while (true);
|
||||
|
||||
Draw_Unlock();
|
||||
menuLeave();
|
||||
}
|
||||
|
||||
@@ -35,16 +35,18 @@
|
||||
#include "menus/debugger.h"
|
||||
#include "menus/miscellaneous.h"
|
||||
#include "menus/sysconfig.h"
|
||||
#include "plgloader.h"
|
||||
#include "ifile.h"
|
||||
#include "memory.h"
|
||||
#include "fmt.h"
|
||||
|
||||
Menu rosalinaMenu = {
|
||||
"Rosalina menu",
|
||||
.nbItems = 10,
|
||||
.nbItems = 11,
|
||||
{
|
||||
{ "New 3DS menu...", MENU, .menu = &N3DSMenu },
|
||||
{ "Cheats...", METHOD, .method = &RosalinaMenu_Cheats },
|
||||
{ "", METHOD, .method = PluginLoader__MenuCallback},
|
||||
{ "Process list", METHOD, .method = &RosalinaMenu_ProcessList },
|
||||
{ "Take screenshot (slow!)", METHOD, .method = &RosalinaMenu_TakeScreenshot },
|
||||
{ "Debugger options...", MENU, .menu = &debuggerMenu },
|
||||
@@ -52,7 +54,7 @@ Menu rosalinaMenu = {
|
||||
{ "Miscellaneous options...", MENU, .menu = &miscellaneousMenu },
|
||||
{ "Power off", METHOD, .method = &RosalinaMenu_PowerOff },
|
||||
{ "Reboot", METHOD, .method = &RosalinaMenu_Reboot },
|
||||
{ "Credits", METHOD, .method = &RosalinaMenu_ShowCredits }
|
||||
{ "Credits", METHOD, .method = &RosalinaMenu_ShowCredits },
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "memory.h"
|
||||
#include "draw.h"
|
||||
#include "hbloader.h"
|
||||
#include "plgloader.h"
|
||||
#include "fmt.h"
|
||||
#include "utils.h" // for makeARMBranch
|
||||
#include "minisoc.h"
|
||||
@@ -168,7 +169,7 @@ void MiscellaneousMenu_ChangeMenuCombo(void)
|
||||
while(!(waitInput() & BUTTON_B) && !terminationRequest);
|
||||
}
|
||||
|
||||
void MiscellaneousMenu_SaveSettings(void)
|
||||
Result SaveSettings(void)
|
||||
{
|
||||
Result res;
|
||||
|
||||
@@ -183,12 +184,14 @@ void MiscellaneousMenu_SaveSettings(void)
|
||||
u32 config, multiConfig, bootConfig;
|
||||
u64 hbldr3dsxTitleId;
|
||||
u32 rosalinaMenuCombo;
|
||||
u32 rosalinaFlags;
|
||||
} configData;
|
||||
|
||||
u32 formatVersion;
|
||||
u32 config, multiConfig, bootConfig;
|
||||
s64 out;
|
||||
bool isSdMode;
|
||||
|
||||
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 2))) svcBreak(USERBREAK_ASSERT);
|
||||
formatVersion = (u32)out;
|
||||
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 3))) svcBreak(USERBREAK_ASSERT);
|
||||
@@ -208,6 +211,7 @@ void MiscellaneousMenu_SaveSettings(void)
|
||||
configData.bootConfig = bootConfig;
|
||||
configData.hbldr3dsxTitleId = HBLDR_3DSX_TID;
|
||||
configData.rosalinaMenuCombo = menuCombo;
|
||||
configData.rosalinaFlags = PluginLoader__IsEnabled();
|
||||
|
||||
FS_ArchiveID archiveId = isSdMode ? ARCHIVE_SDMC : ARCHIVE_NAND_RW;
|
||||
res = IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, "/luma/config.bin"), FS_OPEN_CREATE | FS_OPEN_WRITE);
|
||||
@@ -215,6 +219,14 @@ void MiscellaneousMenu_SaveSettings(void)
|
||||
if(R_SUCCEEDED(res))
|
||||
res = IFile_Write(&file, &total, &configData, sizeof(configData), 0);
|
||||
|
||||
IFile_Close(&file);
|
||||
return res;
|
||||
}
|
||||
|
||||
void MiscellaneousMenu_SaveSettings(void)
|
||||
{
|
||||
Result res = SaveSettings();
|
||||
|
||||
Draw_Lock();
|
||||
Draw_ClearFramebuffer();
|
||||
Draw_FlushFramebuffer();
|
||||
|
||||
@@ -223,8 +223,8 @@ static void ProcessListMenu_MemoryViewer(const ProcessInfo *info)
|
||||
svcQueryProcessMemory(&mem, &out, processHandle, heapStartAddress);
|
||||
heapTotalSize = mem.size;
|
||||
|
||||
Result codeRes = svcMapProcessMemoryEx(processHandle, codeDestAddress, codeStartAddress, codeTotalSize);
|
||||
Result heapRes = svcMapProcessMemoryEx(processHandle, heapDestAddress, heapStartAddress, heapTotalSize);
|
||||
Result codeRes = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, codeDestAddress, processHandle, codeStartAddress, codeTotalSize);
|
||||
Result heapRes = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, heapDestAddress, processHandle, heapStartAddress, heapTotalSize);
|
||||
|
||||
bool codeAvailable = R_SUCCEEDED(codeRes);
|
||||
bool heapAvailable = R_SUCCEEDED(heapRes);
|
||||
@@ -565,9 +565,9 @@ static void ProcessListMenu_MemoryViewer(const ProcessInfo *info)
|
||||
}
|
||||
|
||||
if(codeAvailable)
|
||||
svcUnmapProcessMemoryEx(processHandle, codeDestAddress, codeTotalSize);
|
||||
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, codeDestAddress, codeTotalSize);
|
||||
if(heapAvailable)
|
||||
svcUnmapProcessMemoryEx(processHandle, heapDestAddress, heapTotalSize);
|
||||
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, heapDestAddress, heapTotalSize);
|
||||
|
||||
svcCloseHandle(processHandle);
|
||||
}
|
||||
|
||||
@@ -105,12 +105,12 @@ static u32 ProcessPatchesMenu_PatchUnpatchProcessByName(const char *name, Result
|
||||
s64 textTotalRoundedSize = 0, startAddress = 0;
|
||||
svcGetProcessInfo(&textTotalRoundedSize, processHandle, 0x10002); // only patch .text
|
||||
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
|
||||
if(R_FAILED(res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, textTotalRoundedSize)))
|
||||
if(R_FAILED(res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, processHandle, (u32) startAddress, textTotalRoundedSize)))
|
||||
return res;
|
||||
|
||||
res = func(textTotalRoundedSize);
|
||||
|
||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, textTotalRoundedSize);
|
||||
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, textTotalRoundedSize);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
143
sysmodules/rosalina/source/plgldr.c
Normal file
143
sysmodules/rosalina/source/plgldr.c
Normal file
@@ -0,0 +1,143 @@
|
||||
#include <3ds.h>
|
||||
#include "plgldr.h"
|
||||
#include <string.h>
|
||||
|
||||
static Handle plgLdrHandle;
|
||||
static int plgLdrRefCount;
|
||||
|
||||
Result plgLdrInit(void)
|
||||
{
|
||||
Result res = 0;
|
||||
|
||||
if (AtomicPostIncrement(&plgLdrRefCount) == 0)
|
||||
res = svcConnectToPort(&plgLdrHandle, "plg:ldr");
|
||||
|
||||
if (R_FAILED(res))
|
||||
AtomicDecrement(&plgLdrRefCount);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void plgLdrExit(void)
|
||||
{
|
||||
if (AtomicDecrement(&plgLdrRefCount))
|
||||
return;
|
||||
|
||||
svcCloseHandle(plgLdrHandle);
|
||||
}
|
||||
|
||||
Result PLGLDR__IsPluginLoaderEnabled(bool *isEnabled)
|
||||
{
|
||||
Result res = 0;
|
||||
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(2, 0, 0);
|
||||
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
|
||||
{
|
||||
res = cmdbuf[1];
|
||||
*isEnabled = cmdbuf[2];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Result PLGLDR__SetPluginLoaderState(bool enabled)
|
||||
{
|
||||
Result res = 0;
|
||||
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(3, 1, 0);
|
||||
cmdbuf[1] = (u32)enabled;
|
||||
|
||||
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
|
||||
{
|
||||
res = cmdbuf[1];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Result PLGLDR__SetPluginLoadParameters(PluginLoadParameters *parameters)
|
||||
{
|
||||
Result res = 0;
|
||||
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(4, 2, 4);
|
||||
cmdbuf[1] = (u32)parameters->noFlash;
|
||||
cmdbuf[2] = parameters->lowTitleId;
|
||||
cmdbuf[3] = IPC_Desc_Buffer(256, IPC_BUFFER_R);
|
||||
cmdbuf[4] = (u32)parameters->path;
|
||||
cmdbuf[5] = IPC_Desc_Buffer(32 * sizeof(u32), IPC_BUFFER_R);
|
||||
cmdbuf[6] = (u32)parameters->config;
|
||||
|
||||
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
|
||||
{
|
||||
res = cmdbuf[1];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Result PLGLDR__DisplayMenu(PluginMenu *menu)
|
||||
{
|
||||
Result res = 0;
|
||||
|
||||
u32 nbItems = menu->nbItems;
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(5, 1, 8);
|
||||
cmdbuf[1] = nbItems;
|
||||
cmdbuf[2] = IPC_Desc_Buffer(nbItems, IPC_BUFFER_RW);
|
||||
cmdbuf[3] = (u32)menu->states;
|
||||
cmdbuf[4] = IPC_Desc_Buffer(MAX_BUFFER, IPC_BUFFER_R);
|
||||
cmdbuf[5] = (u32)menu->title;
|
||||
cmdbuf[6] = IPC_Desc_Buffer(MAX_BUFFER * nbItems, IPC_BUFFER_R);
|
||||
cmdbuf[7] = (u32)menu->items;
|
||||
cmdbuf[8] = IPC_Desc_Buffer(MAX_BUFFER * nbItems, IPC_BUFFER_R);
|
||||
cmdbuf[9] = (u32)menu->hints;
|
||||
|
||||
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
|
||||
{
|
||||
res = cmdbuf[1];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Result PLGLDR__DisplayMessage(const char *title, const char *body)
|
||||
{
|
||||
Result res = 0;
|
||||
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(6, 0, 4);
|
||||
cmdbuf[1] = IPC_Desc_Buffer(strlen(title), IPC_BUFFER_R);
|
||||
cmdbuf[2] = (u32)title;
|
||||
cmdbuf[3] = IPC_Desc_Buffer(strlen(body), IPC_BUFFER_R);
|
||||
cmdbuf[4] = (u32)body;
|
||||
|
||||
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
|
||||
{
|
||||
res = cmdbuf[1];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Result PLGLDR__DisplayErrMessage(const char *title, const char *body, u32 error)
|
||||
{
|
||||
Result res = 0;
|
||||
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(7, 1, 4);
|
||||
cmdbuf[1] = error;
|
||||
cmdbuf[2] = IPC_Desc_Buffer(strlen(title), IPC_BUFFER_R);
|
||||
cmdbuf[3] = (u32)title;
|
||||
cmdbuf[4] = IPC_Desc_Buffer(strlen(body), IPC_BUFFER_R);
|
||||
cmdbuf[5] = (u32)body;
|
||||
|
||||
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
|
||||
{
|
||||
res = cmdbuf[1];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
599
sysmodules/rosalina/source/plgloader.c
Normal file
599
sysmodules/rosalina/source/plgloader.c
Normal file
@@ -0,0 +1,599 @@
|
||||
#include <3ds.h>
|
||||
#include "3gx.h"
|
||||
#include "ifile.h"
|
||||
#include "utils.h" // for makeARMBranch
|
||||
#include "plgloader.h"
|
||||
#include "fmt.h"
|
||||
#include "menu.h"
|
||||
#include "menus.h"
|
||||
#include "memory.h"
|
||||
#include "sleep.h"
|
||||
|
||||
#define MEMPERM_RW (MEMPERM_READ | MEMPERM_WRITE)
|
||||
#define MemBlockSize (5*1024*1024) /* 5 MiB */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Result code;
|
||||
const char * message;
|
||||
} Error;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool isEnabled;
|
||||
bool noFlash;
|
||||
u32 titleid;
|
||||
char path[256];
|
||||
u32 config[32];
|
||||
} PluginLoadParameters;
|
||||
|
||||
#define HeaderMagic (0x24584733) /* "3GX$" */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 magic;
|
||||
u32 version;
|
||||
u32 heapVA;
|
||||
u32 heapSize;
|
||||
u32 pluginSize;
|
||||
const char* pluginPathPA;
|
||||
u32 isDefaultPlugin;
|
||||
u32 reserved[25];
|
||||
u32 config[32];
|
||||
} PluginHeader;
|
||||
|
||||
static bool g_isEnabled;
|
||||
static u8 * g_memBlock;
|
||||
static char g_path[256];
|
||||
static Handle g_process = 0;
|
||||
static u32 g_codeSize;
|
||||
static u32 g_heapSize;
|
||||
static Error g_error;
|
||||
static PluginLoadParameters g_userDefinedLoadParameters;
|
||||
|
||||
static MyThread g_pluginLoaderThread;
|
||||
static u8 ALIGN(8) g_pluginLoaderThreadStack[0x4000];
|
||||
|
||||
// pluginLoader.s
|
||||
void gamePatchFunc(void);
|
||||
|
||||
void PluginLoader__ThreadMain(void);
|
||||
MyThread * PluginLoader__CreateThread(void)
|
||||
{
|
||||
s64 out;
|
||||
|
||||
svcGetSystemInfo(&out, 0x10000, 0x102);
|
||||
g_isEnabled = out & 1;
|
||||
g_userDefinedLoadParameters.isEnabled = false;
|
||||
|
||||
if(R_FAILED(MyThread_Create(&g_pluginLoaderThread, PluginLoader__ThreadMain, g_pluginLoaderThreadStack, 0x4000, 20, CORE_SYSTEM)))
|
||||
svcBreak(USERBREAK_PANIC);
|
||||
return &g_pluginLoaderThread;
|
||||
}
|
||||
|
||||
bool PluginLoader__IsEnabled(void)
|
||||
{
|
||||
return g_isEnabled;
|
||||
}
|
||||
|
||||
void PluginLoader__MenuCallback(void)
|
||||
{
|
||||
g_isEnabled = !g_isEnabled;
|
||||
SaveSettings();
|
||||
PluginLoader__UpdateMenu();
|
||||
}
|
||||
|
||||
void PluginLoader__UpdateMenu(void)
|
||||
{
|
||||
static const char *status[2] =
|
||||
{
|
||||
"Plugin Loader: [Disabled]",
|
||||
"Plugin Loader: [Enabled]"
|
||||
};
|
||||
|
||||
rosalinaMenu.items[isN3DS + 1].title = status[g_isEnabled];
|
||||
}
|
||||
|
||||
static Result MapPluginInProcess(Handle proc, u32 size)
|
||||
{
|
||||
u32 heapsize = MemBlockSize - size;
|
||||
Result res;
|
||||
PluginHeader *header = (PluginHeader *)g_memBlock;
|
||||
|
||||
header->heapVA = 0x06000000;
|
||||
g_heapSize = header->heapSize = heapsize;
|
||||
g_codeSize = size;
|
||||
|
||||
// From now on, all memory page mapped to the process should be rwx
|
||||
svcControlProcess(proc, PROCESSOP_SET_MMU_TO_RWX, 0, 0);
|
||||
|
||||
// Plugin
|
||||
if (R_FAILED((res = svcMapProcessMemoryEx(proc, 0x07000000, CUR_PROCESS_HANDLE, (u32)g_memBlock, size))))
|
||||
{
|
||||
g_error.message = "Couldn't map plugin memory block";
|
||||
g_error.code = res;
|
||||
return res;
|
||||
}
|
||||
|
||||
// Heap (to be used by the plugin)
|
||||
if (R_FAILED((res = svcMapProcessMemoryEx(proc, 0x06000000, CUR_PROCESS_HANDLE, (u32)g_memBlock + size, heapsize))))
|
||||
{
|
||||
g_error.message = "Couldn't map heap memory block";
|
||||
g_error.code = res;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Clear heap section
|
||||
memset32(g_memBlock + size, 0, heapsize);
|
||||
|
||||
exit:
|
||||
return res;
|
||||
}
|
||||
|
||||
static u32 strlen16(const u16 *str)
|
||||
{
|
||||
u32 size = 0;
|
||||
|
||||
while (str && *str++) ++size;
|
||||
return size;
|
||||
}
|
||||
|
||||
static Result FindPluginFile(u64 tid)
|
||||
{
|
||||
char filename[256];
|
||||
u32 entriesNb = 0;
|
||||
bool found = false;
|
||||
Handle dir = 0;
|
||||
Result res;
|
||||
FS_Archive sdmcArchive = 0;
|
||||
FS_DirectoryEntry entries[10];
|
||||
|
||||
sprintf(g_path, "/luma/plugins/%016llX", tid);
|
||||
|
||||
if (R_FAILED((res =FSUSER_OpenArchive(&sdmcArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")))))
|
||||
goto exit;
|
||||
|
||||
if (R_FAILED((res = FSUSER_OpenDirectory(&dir, sdmcArchive, fsMakePath(PATH_ASCII, g_path)))))
|
||||
goto exit;
|
||||
|
||||
strcat(g_path, "/");
|
||||
while (!found && R_SUCCEEDED(FSDIR_Read(dir, &entriesNb, 10, entries)))
|
||||
{
|
||||
if (entriesNb == 0)
|
||||
break;
|
||||
|
||||
static const u16 * validExtension = u"3gx";
|
||||
|
||||
for (u32 i = 0; i < entriesNb; ++i)
|
||||
{
|
||||
FS_DirectoryEntry *entry = &entries[i];
|
||||
|
||||
// If entry is a folder, skip it
|
||||
if (entry->attributes & 1)
|
||||
continue;
|
||||
|
||||
// Check extension
|
||||
u32 size = strlen16(entry->name);
|
||||
if (size <= 5)
|
||||
continue;
|
||||
|
||||
u16 *fileExt = entry->name + size - 3;
|
||||
|
||||
if (memcmp(fileExt, validExtension, 3 * sizeof(u16)))
|
||||
continue;
|
||||
|
||||
// Convert name from utf16 to utf8
|
||||
int units = utf16_to_utf8((u8 *)filename, entry->name, 100);
|
||||
if (units == -1)
|
||||
continue;
|
||||
filename[units] = 0;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
res = MAKERESULT(28, 4, 0, 1018);
|
||||
else
|
||||
{
|
||||
u32 len = strlen(g_path);
|
||||
filename[256 - len] = 0;
|
||||
strcat(g_path, filename);
|
||||
}
|
||||
|
||||
((PluginHeader *)g_memBlock)->pluginPathPA = PA_FROM_VA_PTR(g_path);
|
||||
|
||||
exit:
|
||||
FSDIR_Close(dir);
|
||||
FSUSER_CloseArchive(sdmcArchive);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static Result OpenFile(IFile *file, const char *path)
|
||||
{
|
||||
return IFile_Open(file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, path), FS_OPEN_READ);
|
||||
}
|
||||
|
||||
static Result CheckPluginCompatibility(_3gx_Header *header, u32 processTitle)
|
||||
{
|
||||
static char errorBuf[0x100];
|
||||
|
||||
if (header->targets.count == 0)
|
||||
return 0;
|
||||
|
||||
for (u32 i = 0; i < header->targets.count; ++i)
|
||||
{
|
||||
if (header->targets.titles[i] == processTitle)
|
||||
return 0;
|
||||
}
|
||||
|
||||
sprintf(errorBuf, "The plugin - %s -\nis not compatible with this game.\n" \
|
||||
"Contact \"%s\" for more infos.", header->infos.titleMsg, header->infos.authorMsg);
|
||||
g_error.message = errorBuf;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool TryToLoadPlugin(Handle process)
|
||||
{
|
||||
u64 fileSize;
|
||||
u64 tid;
|
||||
u32 procStart = 0x00100000;
|
||||
IFile plugin;
|
||||
PluginHeader *hdr = (PluginHeader *)g_memBlock;
|
||||
_3gx_Header *header;
|
||||
Result res;
|
||||
|
||||
// Clear the memblock
|
||||
memset32(g_memBlock, 0, MemBlockSize);
|
||||
|
||||
hdr->magic = HeaderMagic;
|
||||
|
||||
// Get title id
|
||||
svcGetProcessInfo((s64 *)&tid, process, 0x10001);
|
||||
if (R_FAILED((res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, procStart, process, procStart, 0x1000))))
|
||||
{
|
||||
g_error.message = "Couldn't map process";
|
||||
g_error.code = res;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to open plugin file
|
||||
if (g_userDefinedLoadParameters.isEnabled && (u32)tid == g_userDefinedLoadParameters.titleid)
|
||||
{
|
||||
g_userDefinedLoadParameters.isEnabled = false;
|
||||
if (OpenFile(&plugin, g_userDefinedLoadParameters.path))
|
||||
goto exitFail;
|
||||
hdr->pluginPathPA = PA_FROM_VA_PTR(g_userDefinedLoadParameters.path);
|
||||
memcpy(hdr->config, g_userDefinedLoadParameters.config, 32 * sizeof(u32));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (R_FAILED(FindPluginFile(tid)) || OpenFile(&plugin, g_path))
|
||||
{
|
||||
// Try to open default plugin
|
||||
const char *defaultPath = "/luma/plugins/default.3gx";
|
||||
if (OpenFile(&plugin, defaultPath))
|
||||
goto exitFail;
|
||||
hdr->isDefaultPlugin = 1;
|
||||
hdr->pluginPathPA = PA_FROM_VA_PTR(defaultPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (R_FAILED((res = IFile_GetSize(&plugin, &fileSize))))
|
||||
g_error.message = "Couldn't get file size";
|
||||
|
||||
// Plugins will rarely exceed 1MB so this is fine
|
||||
header = (_3gx_Header *)(g_memBlock + MemBlockSize - (u32)fileSize);
|
||||
|
||||
// Read header
|
||||
if (!res && R_FAILED((res = Read_3gx_Header(&plugin, header))))
|
||||
g_error.message = "Couldn't read file";
|
||||
|
||||
// Check titles compatibility
|
||||
if (!res) res = CheckPluginCompatibility(header, (u32)tid);
|
||||
|
||||
// Read code
|
||||
if (!res && R_FAILED(res = Read_3gx_Code(&plugin, header, g_memBlock + sizeof(PluginHeader))))
|
||||
g_error.message = "Couldn't read plugin's code";
|
||||
|
||||
// Close file
|
||||
IFile_Close(&plugin);
|
||||
if (R_FAILED(res))
|
||||
{
|
||||
g_error.code = res;
|
||||
goto exitFail;
|
||||
}
|
||||
|
||||
hdr->version = header->version;
|
||||
// Code size must be page aligned
|
||||
fileSize = (header->codeSize + 0x1100) & ~0xFFF;
|
||||
|
||||
if (MapPluginInProcess(process, fileSize) == 0)
|
||||
// Install hook
|
||||
{
|
||||
extern u32 g_savedGameInstr[2];
|
||||
u32 *game = (u32 *)procStart;
|
||||
|
||||
g_savedGameInstr[0] = game[0];
|
||||
g_savedGameInstr[1] = game[1];
|
||||
|
||||
game[0] = 0xE51FF004; // ldr pc, [pc, #-4]
|
||||
game[1] = (u32)PA_FROM_VA_PTR(gamePatchFunc);
|
||||
}
|
||||
else
|
||||
goto exitFail;
|
||||
|
||||
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, procStart, 0x1000);
|
||||
return true;
|
||||
|
||||
exitFail:
|
||||
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, procStart, 0x1000);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void SetKernelConfigurationMemoryFlag(bool loaded)
|
||||
{
|
||||
u32 *flag = (u32 *)PA_FROM_VA_PTR(0x1FF800F0);
|
||||
|
||||
*flag = loaded;
|
||||
}
|
||||
|
||||
static void PluginLoader_HandleCommands(void)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
switch (cmdbuf[0] >> 16)
|
||||
{
|
||||
case 1: // Load plugin
|
||||
{
|
||||
if (cmdbuf[0] != IPC_MakeHeader(1, 0, 2))
|
||||
{
|
||||
error(cmdbuf, 0xD9001830);
|
||||
break;
|
||||
}
|
||||
|
||||
Handle process = cmdbuf[2];
|
||||
|
||||
if (g_isEnabled && TryToLoadPlugin(process))
|
||||
{
|
||||
if (!g_userDefinedLoadParameters.isEnabled && g_userDefinedLoadParameters.noFlash)
|
||||
g_userDefinedLoadParameters.noFlash = false;
|
||||
else
|
||||
{
|
||||
for (u32 i = 0; i < 64; i++)
|
||||
{
|
||||
REG32(0x10202204) = 0x01FF9933;
|
||||
svcSleepThread(5000000);
|
||||
}
|
||||
REG32(0x10202204) = 0;
|
||||
}
|
||||
|
||||
g_process = process;
|
||||
}
|
||||
else
|
||||
svcCloseHandle(process);
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(1, 1, 0);
|
||||
cmdbuf[1] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: // Check if plugin loader is enabled
|
||||
{
|
||||
if (cmdbuf[0] != IPC_MakeHeader(2, 0, 0))
|
||||
{
|
||||
error(cmdbuf, 0xD9001830);
|
||||
break;
|
||||
}
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(2, 2, 0);
|
||||
cmdbuf[1] = 0;
|
||||
cmdbuf[2] = (u32)g_isEnabled;
|
||||
break;
|
||||
}
|
||||
|
||||
case 3: // Enable / Disable plugin loader
|
||||
{
|
||||
if (cmdbuf[0] != IPC_MakeHeader(3, 1, 0))
|
||||
{
|
||||
error(cmdbuf, 0xD9001830);
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmdbuf[1] != g_isEnabled)
|
||||
{
|
||||
g_isEnabled = cmdbuf[1];
|
||||
SaveSettings();
|
||||
}
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(3, 1, 0);
|
||||
cmdbuf[1] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case 4: // Define next plugin load settings
|
||||
{
|
||||
if (cmdbuf[0] != IPC_MakeHeader(4, 2, 4))
|
||||
{
|
||||
error(cmdbuf, 0xD9001830);
|
||||
break;
|
||||
}
|
||||
|
||||
g_userDefinedLoadParameters.isEnabled = true;
|
||||
g_userDefinedLoadParameters.noFlash = cmdbuf[1];
|
||||
g_userDefinedLoadParameters.titleid = cmdbuf[2];
|
||||
strncpy(g_userDefinedLoadParameters.path, (const char *)cmdbuf[4], 255);
|
||||
memcpy(g_userDefinedLoadParameters.config, (void *)cmdbuf[6], 32 * sizeof(u32));
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(4, 1, 0);
|
||||
cmdbuf[1] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case 5: // Display menu
|
||||
{
|
||||
if (cmdbuf[0] != IPC_MakeHeader(5, 1, 8))
|
||||
{
|
||||
error(cmdbuf, 0xD9001830);
|
||||
break;
|
||||
}
|
||||
|
||||
u32 nbItems = cmdbuf[1];
|
||||
u32 states = cmdbuf[3];
|
||||
DisplayPluginMenu(cmdbuf);
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(5, 1, 2);
|
||||
cmdbuf[1] = 0;
|
||||
cmdbuf[2] = IPC_Desc_Buffer(nbItems, IPC_BUFFER_RW);
|
||||
cmdbuf[3] = states;
|
||||
break;
|
||||
}
|
||||
|
||||
case 6: // Display message
|
||||
{
|
||||
if (cmdbuf[0] != IPC_MakeHeader(6, 0, 4))
|
||||
{
|
||||
error(cmdbuf, 0xD9001830);
|
||||
break;
|
||||
}
|
||||
|
||||
const char *title = (const char *)cmdbuf[2];
|
||||
const char *body = (const char *)cmdbuf[4];
|
||||
|
||||
DispMessage(title, body);
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(6, 1, 0);
|
||||
cmdbuf[1] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case 7: // Display error message
|
||||
{
|
||||
if (cmdbuf[0] != IPC_MakeHeader(7, 1, 4))
|
||||
{
|
||||
error(cmdbuf, 0xD9001830);
|
||||
break;
|
||||
}
|
||||
|
||||
const char *title = (const char *)cmdbuf[3];
|
||||
const char *body = (const char *)cmdbuf[5];
|
||||
|
||||
DispErrMessage(title, body, cmdbuf[1]);
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(7, 1, 0);
|
||||
cmdbuf[1] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PluginLoader__ThreadMain(void)
|
||||
{
|
||||
const char *title = "Plugin loader";
|
||||
|
||||
Result res = 0;
|
||||
MemOp memRegion = isN3DS ? MEMOP_REGION_BASE : MEMOP_REGION_SYSTEM;
|
||||
Handle handles[3];
|
||||
Handle serverHandle, clientHandle, sessionHandle = 0;
|
||||
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
u32 replyTarget = 0;
|
||||
u32 nbHandle;
|
||||
s32 index;
|
||||
|
||||
// Wait for the system to be completely started
|
||||
{
|
||||
bool isAcuRegistered = false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (R_SUCCEEDED(srvIsServiceRegistered(&isAcuRegistered, "ac:u"))
|
||||
&& isAcuRegistered)
|
||||
break;
|
||||
svcSleepThread(100000);
|
||||
}
|
||||
}
|
||||
|
||||
// Init memory block to hold the plugin
|
||||
{
|
||||
u32 free = (u32)osGetMemRegionFree(memRegion);
|
||||
u32 temp = 0;
|
||||
|
||||
svcControlMemoryEx(&temp, 0x00100000, 0, free - MemBlockSize, memRegion | MEMOP_ALLOC, MEMPERM_RW, true);
|
||||
if (R_FAILED((res = svcControlMemoryEx((u32 *)&g_memBlock, 0x07000000, 0, MemBlockSize, memRegion | MEMOP_ALLOC, MEMPERM_RW, true))))
|
||||
{
|
||||
svcSleepThread(5000000000ULL); ///< Wait until the system started to display the error
|
||||
DispErrMessage(title, "Couldn't allocate memblock", res);
|
||||
return;
|
||||
}
|
||||
svcControlMemoryEx(&temp, (u32)temp, 0, MemBlockSize, memRegion | MEMOP_FREE, 0, true);
|
||||
}
|
||||
|
||||
assertSuccess(svcCreatePort(&serverHandle, &clientHandle, "plg:ldr", 1));
|
||||
|
||||
do
|
||||
{
|
||||
g_error.message = NULL;
|
||||
g_error.code = 0;
|
||||
handles[0] = serverHandle;
|
||||
handles[1] = sessionHandle == 0 ? g_process : sessionHandle;
|
||||
handles[2] = g_process;
|
||||
|
||||
if(replyTarget == 0) // k11
|
||||
cmdbuf[0] = 0xFFFF0000;
|
||||
|
||||
nbHandle = 1 + (sessionHandle != 0) + (g_process != 0);
|
||||
res = svcReplyAndReceive(&index, handles, nbHandle, replyTarget);
|
||||
|
||||
if(R_FAILED(res))
|
||||
{
|
||||
if((u32)res == 0xC920181A) // session closed by remote
|
||||
{
|
||||
svcCloseHandle(sessionHandle);
|
||||
sessionHandle = 0;
|
||||
replyTarget = 0;
|
||||
}
|
||||
else
|
||||
svcBreak(USERBREAK_PANIC);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(index == 0)
|
||||
{
|
||||
Handle session;
|
||||
assertSuccess(svcAcceptSession(&session, serverHandle));
|
||||
|
||||
if(sessionHandle == 0)
|
||||
sessionHandle = session;
|
||||
else
|
||||
svcCloseHandle(session);
|
||||
}
|
||||
else if (index == 1 && handles[1] == sessionHandle)
|
||||
{
|
||||
PluginLoader_HandleCommands();
|
||||
replyTarget = sessionHandle;
|
||||
}
|
||||
else ///< The process in which we injected the plugin is terminating
|
||||
{
|
||||
// Unmap plugin's memory before closing the process
|
||||
svcUnmapProcessMemoryEx(g_process, 0x07000000, g_codeSize);
|
||||
svcUnmapProcessMemoryEx(g_process, 0x06000000, g_heapSize);
|
||||
svcCloseHandle(g_process);
|
||||
g_process = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_error.message != NULL)
|
||||
DispErrMessage(title, g_error.message, g_error.code);
|
||||
|
||||
SetKernelConfigurationMemoryFlag(g_process != 0);
|
||||
|
||||
} while(!terminationRequest);
|
||||
|
||||
svcCloseHandle(sessionHandle);
|
||||
svcCloseHandle(clientHandle);
|
||||
svcCloseHandle(serverHandle);
|
||||
svcControlMemoryEx((u32 *)&g_memBlock, (u32)g_memBlock, 0, MemBlockSize, memRegion | MEMOP_FREE, 0, true);
|
||||
}
|
||||
33
sysmodules/rosalina/source/pluginLoader.s
Normal file
33
sysmodules/rosalina/source/pluginLoader.s
Normal file
@@ -0,0 +1,33 @@
|
||||
.section .data
|
||||
.balign 4
|
||||
.arm
|
||||
|
||||
.global gamePatchFunc
|
||||
.type gamePatchFunc, %function
|
||||
gamePatchFunc:
|
||||
stmfd sp!, {r0-r12, lr}
|
||||
mrs r0, cpsr
|
||||
stmfd sp!, {r0}
|
||||
adr r0, g_savedGameInstr
|
||||
ldr r1, =0x00100000
|
||||
ldr r2, [r0]
|
||||
str r2, [r1]
|
||||
ldr r2, [r0, #4]
|
||||
str r2, [r1, #4]
|
||||
svc 0x92
|
||||
svc 0x94
|
||||
|
||||
startplugin:
|
||||
ldr r5, =0x07000100
|
||||
blx r5
|
||||
|
||||
exit:
|
||||
ldmfd sp!, {r0}
|
||||
msr cpsr, r0
|
||||
ldmfd sp!, {r0-r12, lr}
|
||||
ldr lr, =0x00100000
|
||||
mov pc, lr
|
||||
|
||||
.global g_savedGameInstr
|
||||
g_savedGameInstr:
|
||||
.word 0, 0
|
||||
@@ -55,7 +55,6 @@ bool Sleep__Status(void)
|
||||
if (g_isSleeping)
|
||||
{
|
||||
LightEvent_Wait(&g_onWakeUpEvent);
|
||||
svcSleepThread(1000000000ULL);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -19,6 +19,14 @@ static bool doPublishNotification(ProcessData *processData, u32 notificationId,
|
||||
}
|
||||
}
|
||||
|
||||
// Handle special case for home button notifications on Mode3 O3DS with plugin loaded
|
||||
if ((notificationId == 0x204 || notificationId == 0x205)
|
||||
&& *(u32 *)0x1FF80030 == 3 && *(u32 *)0x1FF800F0)
|
||||
{
|
||||
svcKernelSetState(0x10007, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(processData->nbPendingNotifications < 0x10)
|
||||
{
|
||||
s32 count;
|
||||
|
||||
Reference in New Issue
Block a user