/////////////////////////////////////////////
//* Custom Firmware Extender 2.1 Firmware *//
/////////////////////////////////////////////

#include <psptypes.h>
#include <pspkernel.h>
#include <string.h>
#include <stdio.h>
#include <pspusb.h>
#include <psppower.h>
#include <pspdisplay.h>
#include <pspdisplay_kernel.h>
#include <pspusbstor.h>

#include "../include/systemctrl_se.h"
#include "../include/pspmodulemgr_kernel.h"
#include "../include/systemctrl.h"
#include <pspsysmem_kernel.h>
//#include "../include/kubridge.h"

#include "main.h"
#include "screenshot.h"
#include "conf.h"
#include "translateButtons.h"

#include "blit.h"

PSP_MODULE_INFO("cfe_game_mod", 0x1000, 1, 1);
PSP_MAIN_THREAD_ATTR(0);
PSP_MAIN_THREAD_STACK_SIZE_KB(0);

#define HOSTFSDRIVER_NAME "USBHostFSDriver"
#define HOSTFSDRIVER_PID  (0x1C9)

#define PSP_USBBUS_DRIVERNAME "USBBusDriver"
#define PSP_USBSTOR_DRIVERNAME "USBStor_Driver"
#define PSP_USB_ACTIVATED              0x200

char strBuf0[64];
char strBuf1[64];
char strBuf2[64];
char strBuf3[64];
char strBuf4[64];
char strBuf5[64];
char strBuf6[64];
char strBuf7[64];
char strBuf8[64];
char strBuf9[64];

char strMsg[128];

int showMsg = 0;
int showHelp = 0;

int cpuSpeed = 222;
int firstBoot = 0;
int menuDone = 0;
int kill_display = 0;

int usePspSlim = 0;

char fullpath[30][128];
int num = 0;
int remotejoy =0;

int usbIsInit = 0;
int usbIsActivate = 0;

int showMusicMenuEnabled=0;

SceCtrlData pad;
SceCtrlData padIso;
unsigned int pad_old;

SceUID main_thid;
SceUID display_thid;

CONFIGFILE config;

int build_args(char *args, const char *execfile, int argc, char **argv)
{
	int loc = 0;
	int i;

	strcpy(args, execfile);
	loc += strlen(execfile) + 1;
	for(i = 0; i < argc; i++)
	{
		strcpy(&args[loc], argv[i]);
		loc += strlen(argv[i]) + 1;
	}

	return loc;
}

int loadStartModule(const char *name, int argc, char **argv)
{
	SceUID modid;
	int status;
	char args[128];
	int len;

	modid = sceKernelLoadModule(name, 0, NULL);
	if(modid >= 0)
	{
		len = build_args(args, name, argc, argv);
		modid = sceKernelStartModule(modid, len, (void *) args, &status, NULL);
		char tmp[32];
		strcpy(tmp, "module loaded : ");
		strcat(tmp, name);
		gameBlit(tmp);
	}
	else
	{
		char tmp[32];
		strcpy(tmp, "lsm: Error loading module ");
		strcat(tmp, name);
		gameBlit(tmp);
		printf("lsm: Error loading module %s\n", name);
	}

	return modid;
}

int unloadStopModule(char *modname)
{
	SceModule *mod;

	while ((mod = sceKernelFindModuleByName(modname)))
	{
		sceKernelStopModule(mod->modid, 0, NULL, NULL, NULL);
		sceKernelUnloadModule(mod->modid);
	}

	return 0;

}

void unloadUsbhost() {

	sceUsbDeactivate(HOSTFSDRIVER_PID);
	sceUsbStop(HOSTFSDRIVER_NAME, 0, 0); //sceUsbStop
  	sceUsbStop(PSP_USBBUS_DRIVERNAME, 0, 0);
	unloadStopModule("USBHostFS");
}

int gameBlit(char *msg)
{
	strcpy(strMsg, msg);
	showMsg=1;
	return 0;
}	

void usbhostStart()
{
	int retVal = 0;

	retVal = sceUsbStart(PSP_USBBUS_DRIVERNAME, 0, 0);
	if (retVal != 0) {
		Kprintf("Error starting USB Bus driver (0x%08X)\n", retVal);
		return 0;
	}
	retVal = sceUsbStart(HOSTFSDRIVER_NAME, 0, 0);
	if (retVal != 0) {
		Kprintf("Error starting USB Host driver (0x%08X)\n",
		   retVal);
		return 0;
	}

	retVal = sceUsbActivate(HOSTFSDRIVER_PID);
}

int remotejoyStart()
{
	int ARGC;
	char * argv[ARGC];
	int argc = ARGC;
	
	argv[argc] = NULL;
	
	loadStartModule("ms0:/seplugins/cfe/remotejoy.prx", 1, &argv);
	
	return 0;
}

int display_thread(SceSize args, void *argp)
{
	while(1) 
	{
		if(kill_display) 
		{
			sceKernelExitDeleteThread(0);
		}
		
		if(showMsg)
		{
			blit_string(1,33,strMsg,0xffffff,0x000000);
			showMsg++;
			if(showMsg==600) showMsg=0;			
		}
		
		if(showHelp)
		{
			blit_string(1, 9, "Custom Firmware Extender 2.1",0xffffff,0x000000);
			blit_string(1, 10, "==========================================",0xffffff,0x000000);
			blit_string(1, 12, strBuf0,0xffffff,0x000000);
			blit_string(1, 14, strBuf1,0xffffff,0x000000);
			blit_string(1, 16, strBuf2,0xffffff,0x000000);
			blit_string(1, 18, strBuf3,0xffffff,0x000000);
			blit_string(1, 20, strBuf4,0xffffff,0x000000);
			blit_string(1, 22, strBuf5,0xffffff,0x000000);
			blit_string(1, 24, strBuf6,0xffffff,0x000000);
			blit_string(1, 26, strBuf7,0xffffff,0x000000);
			blit_string(1, 28, strBuf8,0xffffff,0x000000);
			blit_string(1, 30, "Circle = Hide This Help",0xffffff,0x000000);
		}

		if(showMusicMenuEnabled)
		{
			blit_string(1, 9, "Custom Firmware Extender Music Menu",0xffffff,0x000000);
			blit_string(1, 10, "======================================",0xffffff,0x000000);
			blit_string(1, 12, "START => Play/Pause Song",0xffffff,0x000000);
			blit_string(1, 14, "UP => Volume Up",0xffffff,0x000000);
			blit_string(1, 16, "DOWN => Volume Down",0xffffff,0x000000);
			blit_string(1, 18, "L => Previous Song",0xffffff,0x000000);
			blit_string(1, 20, "R => Next Song",0xffffff,0x000000);
			blit_string(1, 22, "SELECT => In-game Mute/Unmute",0xffffff,0x000000);
			blit_string(1, 24, "RIGHT => Random Playback",0xffffff,0x000000);
			blit_string(1, 26, "LEFT => Loop Playback",0xffffff,0x000000);
			blit_string(1, 28, "CIRCLE = Exit Menu",0xffffff,0x000000);
		}

		sceKernelDelayThread(2000);
	}
	return 0;
}

void setCpuSpeed(void)
{
	switch(cpuSpeed) {

		case 111:
			scePowerSetClockFrequency(111, 111, 55);
			cpuSpeed=222;
			sceKernelDelayThread(10000);
			if(!firstBoot) {
				sprintf(strBuf0,"Cpu/Bus Speed => %03d/%03d",scePowerGetCpuClockFrequency(),scePowerGetBusClockFrequency());
				gameBlit(strBuf0);
			}			
			break;		
	
		case 222:
			scePowerSetClockFrequency(222, 222, 111);
			cpuSpeed=266;
			sceKernelDelayThread(10000);
			if(!firstBoot) {
				sprintf(strBuf0,"Cpu/Bus Speed => %03d/%03d",scePowerGetCpuClockFrequency(),scePowerGetBusClockFrequency());
				gameBlit(strBuf0);
			}			
			break;
					
		case 266:
			scePowerSetClockFrequency(266, 266, 133);
			cpuSpeed=333;
			sceKernelDelayThread(10000);
			if(!firstBoot) {
				sprintf(strBuf0,"Cpu/Bus Speed => %03d/%03d",scePowerGetCpuClockFrequency(),scePowerGetBusClockFrequency());
				gameBlit(strBuf0);
			}
			break;
	
		case 333:
			scePowerSetClockFrequency(333, 333, 166);
			cpuSpeed=66;
			sceKernelDelayThread(10000);
			if(!firstBoot) {
				sprintf(strBuf0,"Cpu/Bus Speed => %03d/%03d",scePowerGetCpuClockFrequency(),scePowerGetBusClockFrequency());
				gameBlit(strBuf0);
			}
			break;
	
		case 66:
			scePowerSetClockFrequency(66, 66, 33);
			cpuSpeed=111;
			sceKernelDelayThread(10000);
			if(!firstBoot) {
				sprintf(strBuf0,"Cpu/Bus Speed => %03d/%03d",scePowerGetCpuClockFrequency(),scePowerGetBusClockFrequency());
				gameBlit(strBuf0);
			}
			break;
	}
}

int helpMenu() 
{	
	menuDone=0;
	
	sprintf(strBuf0, "%s + %s = Shutdown PSP", config.comboButton, config.poweroffButton);
	sprintf(strBuf1, "%s + %s = Reboot to %s", config.comboButton, config.resetButton1, config.resetButton1_TM_BOOT);
	sprintf(strBuf2, "%s + %s = Reboot to %s", config.comboButton, config.resetButton2, config.resetButton2_TM_BOOT);
	sprintf(strBuf3, "%s + %s = Change Cpu Speed", config.comboButton, config.cpuspeedButton);
	sprintf(strBuf4, "%s + %s = Set Max Brightness", config.comboButton, config.maxBrightnessButton);
	sprintf(strBuf5, "%s + %s = Start/Stop Usb Mass", config.comboButton, config.startStopUsbmassButton);
	sprintf(strBuf6, "%s + %s = Take Screenshot", config.comboButton, config.takeScreenshotButton);
	sprintf(strBuf7, "%s + %s = Start RemoteJoy (video capture)", config.comboButton, config.captureButton);
	sprintf(strBuf8, "%s + %s = Show this help", config.comboButton, config.showHelpButton);	
		
	while(!menuDone)
	{
		//cleanBeforeExit();
		
		showHelp=1;
		
		sceDisplayWaitVblankStart();	
		
		sceCtrlPeekBufferPositive(&pad, 1);
		if (pad.Buttons != pad_old)
		{	
			if(pad.Buttons & PSP_CTRL_CIRCLE) 
			{
				showHelp=0;
				menuDone=1;
			}
		}
	}
	pad_old = pad.Buttons;
	sceKernelDelayThread(1000);
	return 0;
}

void setBrightness(int x)
{
	sceDisplaySetBrightness(x,0);
}

int usbInit(){

	u32 retVal;

	//start necessary drivers
	loadStartModule("flash0:/kd/chkreg.prx", 0, NULL);   
	loadStartModule("flash0:/kd/npdrm.prx", 0, NULL);
	loadStartModule("flash0:/kd/semawm.prx", 0, NULL);
	loadStartModule("flash0:/kd/usbstor.prx", 0, NULL);
	loadStartModule("flash0:/kd/usbstormgr.prx", 0, NULL);
	loadStartModule("flash0:/kd/usbstorms.prx", 0, NULL);
	loadStartModule("flash0:/kd/usbstorboot.prx", 0, NULL);

	//setup USB drivers
	retVal = sceUsbStart(PSP_USBBUS_DRIVERNAME, 0, 0);
	if (retVal != 0) {
		return -6;
	}

	retVal = sceUsbStart(PSP_USBSTOR_DRIVERNAME, 0, 0);
	if (retVal != 0) {
		return -7;
	}

	retVal = sceUsbstorBootSetCapacity(0x800000);
	if (retVal != 0) {
		return -8;
	}

    return 0;
}

int usbActivate()
{
	sceUsbActivate(0x1c8);
	return 0;
}

int usbDeactivate()
{
 	sceUsbDeactivate(0x1c8);
	sceIoDevctl("fatms0:", 0x0240D81E, NULL, 0, NULL, 0 ); //Avoid corrupted files
	return 0;
}

int loadUsb()
{
	if(!usbIsInit)
	{
		usbInit();
		sceKernelDelayThread(10000);
		usbActivate();
		usbIsInit=1;
		usbIsActivate=1;
	}
	else
	{
		if(usbIsActivate)
		{
			usbDeactivate();
			usbIsActivate=0;
		}
		else
		{
			usbActivate();
			usbIsActivate=1;
		}		
	}
}

int musicModuleLoaded=0;

const char* gameMusicDir()
{
        const char* p;
        sceKernelDelayThread(10000);
 
        p = config.music_dir;
        return p; 
}

const char* gameMusicButton()
{
        const char* q;
        sceKernelDelayThread(10000);
 
        q = config.comboButton;
        return q; 
}

const char* gameComboButton()
{
        const char* r;
        sceKernelDelayThread(10000);
 
        r = config.musicButton;
        return r; 
}

int threadMain(SceSize args, void *argp)
{
	firstBoot = 1;
	
	read_config("ms0:/seplugins/cfe/cfe.config", &config);
	sceKernelDelayThread(10000);
	
	while(!translateButtons()) {
		printf("waiting\n");
	}
	
	cpuSpeed=config.defaultCpuSpeedGame;
	setCpuSpeed();

	sceKernelDelayThread(30*100000);
	
	if(defaultBrightnessEnable) setBrightness(config.defaultBrightnessGame);
	
	firstBoot=0;

	while(1) {
	
		//cleanBeforeExit();

		sceCtrlPeekBufferPositive(&pad, 1);
		
		if (pad.Buttons != pad_old)
		{		
			if(poweroffEnable) 
			{
				if((pad.Buttons & (poweroffButton_i | comboButton_i)) == (poweroffButton_i | comboButton_i))				
				{
					scePowerRequestStandby();
				}
			}

			if(reset1Enable) 
			{	
				if((pad.Buttons & (resetButton1_i | comboButton_i)) == (resetButton1_i | comboButton_i))
				{
					int fd = sceIoOpen("ms0:/TM/config.txt", PSP_O_CREAT | PSP_O_RDWR, 0777);
					if(fd < 0) 
					{
						sceIoClose(fd);
					}
					else
					{
						char temp[48];
						strcpy(temp, "NOTHING = \"");
						strcat(temp, config.resetButton1_TM_BOOT);
						strcat(temp, "\";");
						sceIoWrite(fd, temp, 48);
						sceIoClose(fd);
					}
					sceKernelDelayThread(10000);
					scePower_driver_0442D852();
				}	
			}
			if(reset2Enable) 
			{	
				if((pad.Buttons & (resetButton2_i | comboButton_i)) == (resetButton2_i | comboButton_i))
				{
					int fd = sceIoOpen("ms0:/TM/config.txt", PSP_O_CREAT | PSP_O_RDWR, 0777);
					if(fd < 0) 
					{
						sceIoClose(fd);
					}
					else
					{
						char temp[48];
						strcpy(temp, "NOTHING = \"");
						strcat(temp, config.resetButton2_TM_BOOT);
						strcat(temp, "\";");
						sceIoWrite(fd, temp, 48);
						sceIoClose(fd);
					}
					sceKernelDelayThread(10000);
					scePower_driver_0442D852();
				}	
			}
			
			if(cpuspeedEnable) {
				if((pad.Buttons & (cpuspeedButton_i | comboButton_i)) == (cpuspeedButton_i | comboButton_i))			
				{
					setCpuSpeed();
				}
			}
	
			if(maxBrightnessEnable) 
			{	
				if((pad.Buttons & (maxBrightnessButton_i | comboButton_i)) == (maxBrightnessButton_i | comboButton_i))
				{
					setBrightness(99);
					gameBlit("Brightness set to maximum value");
				}
			}
			
			if(takeScreenshotEnable) 
			{
				if((pad.Buttons & (takeScreenshotButton_i | comboButton_i)) == (takeScreenshotButton_i | comboButton_i))			
				{
					//screenshot(config.captureFolder);
					screenshot();
					sprintf(strBuf0,"Screenshot saved to %s", file);
					gameBlit(strBuf0);
				}	
			}
			
			if(showHelpEnable)
			{
				if((pad.Buttons & (showHelpButton_i | comboButton_i)) == (showHelpButton_i | comboButton_i))
				{
					helpMenu();
				}
			}
			
			if(captureEnable) 
			{
				if((pad.Buttons & (captureButton_i | comboButton_i)) == (captureButton_i | comboButton_i))
				{
					if(!remotejoy) 
					{
						gameBlit("Starting remotejoy video capture plugin");
						sceKernelDelayThread(10*100000);
						loadStartModule("ms0:/seplugins/cfe/usbhostfs.prx", 0, NULL);	
						sceKernelDelayThread(20*100000);	
						//remotejoyStart();
						loadStartModule("ms0:/seplugins/cfe/remotejoy.prx", 0, NULL);
						sceKernelDelayThread(20*100000);
	
						remotejoy=1;	
						gameBlit("Done");
							
					} 
					else
					{
						gameBlit("Stoping remotejoy video capture plugin");	
						remotejoyShutdown(); // exported by remotejoy.prx
						//unloadStopModule("RemoteJoy");
						unloadUsbhost();
						remotejoy=0;
					}
				}
			}
			
			/*if((pad.Buttons & (PSP_CTRL_NOTE | PSP_CTRL_LTRIGGER)) == (PSP_CTRL_NOTE | PSP_CTRL_LTRIGGER))
			{
				loadStartModule("ms0:/seplugins/cfe/fps.prx", 0, NULL);	
			}*/

			if(startStopUsbmassEnable) {
				if((pad.Buttons & (startStopUsbmassButton_i | comboButton_i)) == (startStopUsbmassButton_i | comboButton_i))
				{
					loadUsb();				
				}
			}
			if(musicButtonEnable)
			{
				if((pad.Buttons & (musicButton_i | comboButton_i)) == (musicButton_i | comboButton_i))	
				{
					if(!musicModuleLoaded) loadStartModule("ms0:/seplugins/cfe/cfe_music.prx", 0, NULL);
					musicModuleLoaded=1;
					showMusicMenuEnabled=1;
					while(!menuDone)
					{
						sceCtrlPeekBufferPositive(&pad, 1);
						if (pad.Buttons != pad_old)
						{	
							if(pad.Buttons & PSP_CTRL_CIRCLE) 
							{
								
								showMusicMenuEnabled=0;
								//menuDone=1;
								break;
							}
						}
						pad_old = pad.Buttons;
						sceKernelDelayThread(1000);
					}
					menuDone=0;
				}
			}
			
		}
		pad_old = pad.Buttons;
		sceKernelDelayThread(1000);
	}
	return 0;
}

int module_start(SceSize args, void *argp)
{
	
	display_thid = sceKernelCreateThread("cfe_game_display", display_thread, 8, 0x200, 0, NULL);
	if(display_thid >= 0) sceKernelStartThread(display_thid, args, argp);
	
	nkLoad();
	
	main_thid = sceKernelCreateThread("cfe_game_main", threadMain, 6, 0x800, 0, NULL);
	if(main_thid >= 0) sceKernelStartThread(main_thid, args, argp);

	return 0;

	sceKernelExitDeleteThread(0);
}

int module_stop(SceSize args, void *argp)
{
	return 0;
}
