/*
	Downgrade Control -> Restore kernel patches and enforce updater launch
	by Davee
	
	31/12/2010
*/

#include <pspkernel.h>
#include <pspsysmem_kernel.h>

#include <stdio.h>
#include <string.h>
#include <systemctrl.h>

#include "utils.h"
#include "patch_table.h"
#include "decrypt.h"

PSP_MODULE_INFO("DowngraderCTRL", 0x3007, 1, 0);

/* function pointers */
int (* PrologueModule)(void *modmgr_param, SceModule *mod) = NULL;
STMOD_HANDLER previous = NULL;

int OnModuleStart(SceModule *mod)
{	
	if (strcmp(mod->modname, "sceMesgLed") == 0)
	{
		PatchMesgled(mod->text_addr);
		ClearCaches();
	}
	else if (strcmp(mod->modname, "updater") == 0)
	{	
		/* Patch the syscall using local functions */
		PatchSyscall(FindFunc("sceMesgLed", "sceResmgr", 0x9DC14891), sceResmgr_9DC14891_patched);
		//PatchSyscall(FindFunc("sceIOFileManager", "IoFileMgrForUser", 0x109F50BC), sceIoOpenPatched);
		//PatchSyscall(FindFunc("sceIOFileManager", "IoFileMgrForUser", 0xACE946E8), sceIoGetstatPatched);
		ClearCaches();
	}
	
	if (previous)
		return previous(mod);
	
	/* else just return 0 */
	return 0;
}

int PrologueModulePatched(void *modmgr_param, SceModule *mod)
{
	/* modmgr_param has changed from 1.50 so I have no included the structure defintion, for an updated version a re-reverse of 6.30 modulemgr is required */
	int res = PrologueModule(modmgr_param, mod);
	
	/* If this function errors, the module is shutdown so we better check for it */
	if (res >= 0)
	{
		/* Pass the module through the OnModuleStart chain */
		OnModuleStart(mod);
	}
	
	/* return success */
	return res;
}

static void PatchModuleManager(void)
{
	/* find the modulemgr module */
	SceModule *mod = sceKernelFindModuleByName("sceModuleManager");
	u32 text_addr = mod->text_addr;
	
	/* link the original calls before hook */
	PrologueModule = (void *)(text_addr + g_patch_table.prologue_module_func);
	
	/* Patch call to PrologueModule from the StartModule function to allow a full coverage of loaded modules (even those without an entry point) */
	MAKE_CALL(text_addr + g_patch_table.prologue_module_call, PrologueModulePatched);
}

static void PatchLoadCore(void)
{
	/* Find the loadcore module */
	SceModule *mod = sceKernelFindModuleByName("sceLoaderCore");
	u32 text_addr = mod->text_addr;
	
	/* Relink the memlmd calls (that reboot destroyed) */
	MAKE_CALL(text_addr + g_patch_table.memlmd_call[0], text_addr + g_patch_table.memlmd_stub[0]);
	MAKE_CALL(text_addr + g_patch_table.memlmd_call[1], text_addr + g_patch_table.memlmd_stub[1]);

	/* if >= 6.30 we need patches for the decryption */
	if (sceKernelDevkitVersion() >= 0x06030010)
	{
		/* we need to hook updater decryption */
		MAKE_CALL(text_addr + g_patch_table.memlmd_call[0], memlmd_7CF1CD3E_patched);
		MAKE_CALL(text_addr + g_patch_table.updater_decrypt_call, sceMesgLed_driver_81F72B1F_patched);
		
		sceMesgLed_driver_81F72B1F = (void *)(text_addr + g_patch_table.updater_decrypt_func);
	}
}

int module_start(SceSize argsize, void *argp)
{
	/* check if we're running as a plugin or in boot */
	if (sceKernelFindModuleByName("sceInit"))
	{
		/* plugin, assume we've got HEN (and an M33 nid resolver) */
		/* check for >= 6.30 */
		if (sceKernelDevkitVersion() >= 0x06030010)
		{
			/* this is firmware dependant :/ so not supported */
			return 0;
		}
		
		/* register with sctrl */
		previous = sctrlHENSetStartModuleHandler(OnModuleStart);
	}
	else
	{
		/* boot */
		/* get patches and nids from loader */
		if (CopyPatchTable(&g_patch_table, (void *)PATCH_TABLE_ADDR_START, sceKernelDevkitVersion()) == 0)
		{
			/* no patches, no go */
			return 0;
		}
		
		/* perform main system patches */
		PatchLoadCore();
		PatchModuleManager();
	}

	/* Clear the caches and return success */
	ClearCaches();
	return 0;
}
