2023-07-24 15:53:08 +02:00

281 lines
8.8 KiB
C

#include <3ds.h>
#include <string.h>
#include <stdio.h>
#include "plugin.h"
#include "ifile.h"
#include "utils.h"
#define MEMPERM_RW (MEMPERM_READ | MEMPERM_WRITE)
u32 g_loadSaveSwapArgs[4];
char g_swapFileName[256];
u32 g_memBlockSize = 5 * 1024 * 1024;
Result MemoryBlock__SetSize(u32 size) {
PluginLoaderContext *ctx = &PluginLoaderCtx;
MemoryBlock *memblock = &ctx->memblock;
if (memblock->isReady)
return MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, RM_LDR, RD_ALREADY_INITIALIZED);
g_memBlockSize = size;
return 0;
}
Result MemoryBlock__IsReady(void)
{
PluginLoaderContext *ctx = &PluginLoaderCtx;
MemoryBlock *memblock = &ctx->memblock;
if (memblock->isReady)
return 0;
Result res;
if (isN3DS || (ctx->pluginMemoryStrategy == PLG_STRATEGY_MODE3))
{
s64 appRegionSize = 0;
s64 appRegionUsed = 0;
u32 appRegionFree;
u32 gameReserveSize;
vu32* appMemAllocPtr = (vu32 *)PA_FROM_VA_PTR(0x1FF80040);
u32 appMemAlloc = *appMemAllocPtr;
u32 temp;
memblock->isAppRegion = true;
svcGetSystemInfo(&appRegionSize, 0x10000, 0x80);
svcGetSystemInfo(&appRegionUsed, 0, 1);
appRegionFree = appRegionSize - appRegionUsed;
// Check if appmemalloc reports the entire available memory
if ((u32)appRegionSize == appMemAlloc)
*appMemAllocPtr -= g_memBlockSize; ///< Remove plugin share from available memory
gameReserveSize = appRegionFree - g_memBlockSize;
// First reserve the game memory size (to avoid heap relocation)
res = svcControlMemoryUnsafe((u32 *)&temp, 0x30000000,
gameReserveSize, MEMOP_REGION_APP | MEMOP_ALLOC | MEMOP_LINEAR_FLAG, MEMPERM_RW);
// Then allocate our plugin memory block
if (R_SUCCEEDED(res))
res = svcControlMemoryUnsafe((u32 *)&memblock->memblock, 0x07000000,
g_memBlockSize, MEMOP_REGION_APP | MEMOP_ALLOC | MEMOP_LINEAR_FLAG, MEMPERM_RW);
// Finally release game reserve block
if (R_SUCCEEDED(res))
res = svcControlMemoryUnsafe((u32 *)&temp, temp, gameReserveSize, MEMOP_FREE, 0);
}
else
{
memblock->isAppRegion = false;
res = svcControlMemoryUnsafe((u32 *)&memblock->memblock, 0x07000000,
g_memBlockSize, MEMOP_REGION_SYSTEM | MEMOP_ALLOC | MEMOP_LINEAR_FLAG, MEMPERM_RW);
}
if (R_FAILED(res)) {
if (isN3DS || (ctx->pluginMemoryStrategy == PLG_STRATEGY_MODE3))
PluginLoader__Error("Cannot map plugin memory.", res);
else
PluginLoader__Error("A console reboot is needed to\nclose extended memory games.\n\nPress [B] to reboot.", res);
svcKernelSetState(7);
}
else
{
// Clear the memblock
memset(memblock->memblock, 0, g_memBlockSize);
memblock->isReady = true;
/*if (isN3DS)
{
// Check if appmemalloc reports the entire available memory
s64 appRegionSize = 0;
vu32* appMemAlloc = (vu32 *)PA_FROM_VA_PTR(0x1FF80040);
svcGetSystemInfo(&appRegionSize, 0x10000, 0x80);
if ((u32)appRegionSize == *appMemAlloc)
*appMemAlloc -= g_memBlockSize; ///< Remove plugin share from available memory
} */
}
return res;
}
Result MemoryBlock__Free(void)
{
MemoryBlock *memblock = &PluginLoaderCtx.memblock;
if (!memblock->isReady)
return 0;
MemOp memRegion = memblock->isAppRegion ? MEMOP_REGION_APP : MEMOP_REGION_SYSTEM;
Result res = svcControlMemoryUnsafe((u32 *)&memblock->memblock, (u32)memblock->memblock,
g_memBlockSize, memRegion | MEMOP_FREE, 0);
memblock->isReady = false;
memblock->memblock = NULL;
if (R_FAILED(res))
PluginLoader__Error("Couldn't free memblock", res);
return res;
}
#define FS_OPEN_RWC (FS_OPEN_READ | FS_OPEN_WRITE | FS_OPEN_CREATE)
Result MemoryBlock__ToSwapFile(void)
{
MemoryBlock *memblock = &PluginLoaderCtx.memblock;
PluginLoaderContext *ctx = &PluginLoaderCtx;
u64 written = 0;
u64 toWrite = g_memBlockSize;
IFile file;
Result res = 0;
svcFlushDataCacheRange(memblock->memblock, g_memBlockSize);
res = IFile_Open(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""),
fsMakePath(PATH_ASCII, g_swapFileName), FS_OPEN_RWC);
if (R_FAILED(res)) {
PluginLoader__Error("CRITICAL: Failed to open swap file.\n\nConsole will now reboot.", res);
svcKernelSetState(7);
}
if (!ctx->isSwapFunctionset) {
PluginLoader__Error("CRITICAL: Swap save function\nis not set.\n\nConsole will now reboot.", res);
svcKernelSetState(7);
}
ctx->swapLoadChecksum = saveSwapFunc(memblock->memblock, memblock->memblock + g_memBlockSize, g_loadSaveSwapArgs);
res = IFile_Write(&file, &written, memblock->memblock, toWrite, FS_WRITE_FLUSH);
if (R_FAILED(res) || written != toWrite) {
PluginLoader__Error("CRITICAL: Couldn't write swap to SD.\n\nConsole will now reboot.", res);
svcKernelSetState(7);
}
IFile_Close(&file);
return res;
}
Result MemoryBlock__FromSwapFile(void)
{
MemoryBlock *memblock = &PluginLoaderCtx.memblock;
u64 read = 0;
u64 toRead = g_memBlockSize;
IFile file;
Result res = 0;
res = IFile_Open(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""),
fsMakePath(PATH_ASCII, g_swapFileName), FS_OPEN_READ);
if (R_FAILED(res)) {
PluginLoader__Error("CRITICAL: Failed to open swap file.\n\nConsole will now reboot.", res);
svcKernelSetState(7);
}
res = IFile_Read(&file, &read, memblock->memblock, toRead);
if (R_FAILED(res) || read != toRead) {
PluginLoader__Error("CRITICAL: Couldn't read swap from SD.\n\nConsole will now reboot.", res);
svcKernelSetState(7);
}
u32 checksum = loadSwapFunc(memblock->memblock, memblock->memblock + g_memBlockSize, g_loadSaveSwapArgs);
PluginLoaderContext *ctx = &PluginLoaderCtx;
if (checksum != ctx->swapLoadChecksum) {
res = -1;
PluginLoader__Error("CRITICAL: Swap file is corrupted.\n\nConsole will now reboot.", res);
svcKernelSetState(7);
}
svcFlushDataCacheRange(memblock->memblock, g_memBlockSize);
IFile_Close(&file);
return res;
}
Result MemoryBlock__MountInProcess(void)
{
Handle target = PluginLoaderCtx.target;
Error *error = &PluginLoaderCtx.error;
PluginHeader *header = &PluginLoaderCtx.header;
MemoryBlock *memblock = &PluginLoaderCtx.memblock;
Result res = 0;
// Executable
if (R_FAILED((res = svcMapProcessMemoryEx(target, 0x07000000, CUR_PROCESS_HANDLE, (u32)memblock->memblock, header->exeSize))))
{
error->message = "Couldn't map exe memory block";
error->code = res;
return res;
}
// Heap (to be used by the plugin)
if (R_FAILED((res = svcMapProcessMemoryEx(target, header->heapVA, CUR_PROCESS_HANDLE, (u32)memblock->memblock + header->exeSize, header->heapSize))))
{
error->message = "Couldn't map heap memory block";
error->code = res;
}
return res;
}
Result MemoryBlock__UnmountFromProcess(void)
{
Handle target = PluginLoaderCtx.target;
PluginHeader *header = &PluginLoaderCtx.header;
Result res = 0;
res = svcUnmapProcessMemoryEx(target, 0x07000000, header->exeSize);
res |= svcUnmapProcessMemoryEx(target, header->heapVA, header->heapSize);
return res;
}
Result MemoryBlock__SetSwapSettings(u32* func, bool isLoad, u32* params)
{
u32* physAddr = PA_FROM_VA_PTR(isLoad ? (u32)loadSwapFunc : (u32)saveSwapFunc); //Bypass mem permissions
memcpy(g_loadSaveSwapArgs, params, sizeof(g_loadSaveSwapArgs));
int i = 0;
for (; i < 32 && func[i] != 0xE320F000; i++)
physAddr[i] = func[i];
if (i >= 32) {
return -1;
}
return 0;
}
void MemoryBlock__ResetSwapSettings(void)
{
u32* savePhysAddr = PA_FROM_VA_PTR((u32)saveSwapFunc); //Bypass mem permissions
u32* loadPhysAddr = PA_FROM_VA_PTR((u32)loadSwapFunc);
PluginLoaderContext *ctx = &PluginLoaderCtx;
memset(g_loadSaveSwapArgs, 0, sizeof(g_loadSaveSwapArgs));
savePhysAddr[0] = loadPhysAddr[0] = 0xE12FFF1E; // BX LR
for (int i = 1; i < 32; i++) {
savePhysAddr[i] = loadPhysAddr[i] = 0xE320F000; // NOP
}
strcpy(g_swapFileName, "/luma/plugins/.swap");
ctx->isSwapFunctionset = false;
svcInvalidateEntireInstructionCache();
}
PluginHeader* MemoryBlock__GetMappedPluginHeader() {
MemoryBlock *memblock = &PluginLoaderCtx.memblock;
return memblock->isReady ? (PluginHeader*)memblock->memblock : NULL;
}