/*
 * PSPLINK
 * -----------------------------------------------------------------------
 * Licensed under the BSD license, see LICENSE in PSPLINK root for details.
 *
 * main.c - PSPLINK Debug/Impose menu
 *
 * Copyright (c) 2006 James F <tyranid@gmail.com>
 *
 * $HeadURL: svn://ps2dev.org/psp/trunk/psplinkusb/tools/debugmenu/main.c $
 * $Id: main.c 2018 2006-10-07 16:54:19Z tyranid $
 */
#include <pspkernel.h>
#include <pspdebug.h>
#include <pspkdebug.h>
#include <pspctrl_kernel.h>
#include <pspdisplay.h>
#include <pspdisplay_kernel.h>
#include <pspsdk.h>
#include <pspctrl.h>
#include <psppower.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pspthreadman.h>
#include <pspsysmem.h>
#include <pspusb.h>
#include <pspusbstor.h>
#include <pspsyscon.h>
#include <pspiofilemgr.h>

#include "conf.h"
#include "screenshot.h"

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

PSP_MODULE_INFO("cfe_vsh_mod", 0x1000, 1, 1);

char strBuf[8];
char strBuf2[32];
int showCpu = 0;
int showCaputreVideo = 0;
int cpuSpeed = 222;
int showHelp = 0;
int showScreenshot = 0;
int showWebBrowserLoaded = 0;

int usbMass = 0;
int usbModules = 0;
int showUsbMassStart = 0;
int showUsbMassStop = 0;
int firstBoot = 0;
int stopSleepMode = 0;

int main_thid;
int display_thid;

u32 state;
u32 retVal;

CONFIGFILE config;

/////////////////////////////////////////////////////////////////////////////
// blit text
/////////////////////////////////////////////////////////////////////////////
extern u8 msx[];

int blit_string(int sx,int sy,const char *msg,int fg_col,int bg_col)
{
	int x,y,p;
	int offset;
	char code;
	unsigned char font;

	int pwidth, pheight, bufferwidth, pixelformat, unk;
	unsigned int* vram32;
   	sceDisplayGetMode(&unk, &pwidth, &pheight);
   	sceDisplayGetFrameBuf((void*)&vram32, &bufferwidth, &pixelformat, &unk);

    if(bufferwidth == 0)
        return;
    if(pixelformat != 3)//this has problems when pixel format == 3 (in gta vcs anyway)
    {
        pspDebugScreenSetColorMode(pixelformat);
        pspDebugScreenSetOffset(0);
        pspDebugScreenSetBase(0x44000000);

        pspDebugScreenSetBackColor(bg_col);
        pspDebugScreenSetTextColor(fg_col);
        pspDebugScreenSetXY(sx,sy);
        return pspDebugScreenPuts(msg);
     }
     else
     {
        sy*=8;
    	for(x=0;msg[x] && x<(pwidth/8);x++)
    	{
    		code = msg[x] & 0x7f; // 7bit ANK
    		for(y=0;y<7;y++)
    		{
    			offset = (sy+y)*bufferwidth + (sx+x)*8;
    			font = msx[ code*8 + y ];
    			for(p=0;p<8;p++)
    			{
    				vram32[offset] = (font & 0x80) ? fg_col : bg_col;
    				font <<= 1;
    				offset++;
    			}
    		}
    	}
    	return x;
    }
}

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[1024];
	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);
	}
	else
	{
		printf("lsm: Error loading module %s\n", name);
	}

	return modid;
}

void setCpuSpeed(void)
{
	switch(cpuSpeed) {
						
		case 111:
			if(!firstBoot) showCpu=1;
			scePowerSetClockFrequency(111,111,55); 
			cpuSpeed=222;
			sceKernelDelayThread(10000);
			break;	
					
		case 222:
			if(!firstBoot) showCpu=1;
			scePowerSetClockFrequency(222,222,111); 
			cpuSpeed=266;
			sceKernelDelayThread(10000);
			break;
	
		case 266:
			if(!firstBoot) showCpu=1;
			scePowerSetClockFrequency(266,266,133); 
			cpuSpeed=333;
			sceKernelDelayThread(10000);
			break;
	
		case 333:
			if(!firstBoot) showCpu=1;
			scePowerSetClockFrequency(333,333,166);
			cpuSpeed=111;
			sceKernelDelayThread(10000);
			break;
	}
}


int showDebug = 0;

int remapUsbhostfs() {

	loadStartModule("ms0:/seplugins/usbhostfs.prx", 0, NULL);
					
	int retVal = 0;

	retVal = sceUsbStart(PSP_USBBUS_DRIVERNAME, 0, 0);
	if (retVal != 0) {
		printf("Error starting USB Bus driver\n");
		return 0;
	}
	retVal = sceUsbStart(HOSTFSDRIVER_NAME, 0, 0);
	if (retVal != 0) {
		printf("Error starting USB Host driver\n");
		return 0;
	}
	retVal = sceUsbActivate(HOSTFSDRIVER_PID);

	sceKernelDelayThread(1000);

	SceModule *mod;
	mod = sceKernelFindModuleByName("sceIOFileManager");
	if(mod == NULL)	printf("Hum ... sceIOFileManager module not found\n");
	
	u32 * addr;
	addr = (u32 *)(mod->gp_value+0x6148);

	printf("hack address : %x\n",addr);
	memset(addr, 0, 76);
 
	sceKernelDelayThread(100*100000);
	showDebug = 1;

	sceIoUnassign("ms0:");
	sceIoUnassign("msoem0:");

	sceIoAssign("ms0:", "host0:", NULL, IOASSIGN_RDWR, NULL, 0);
	sceIoAssign("msoem0:", "host0:", NULL, IOASSIGN_RDWR, NULL, 0);
	
	return 1;
}

int loadUsb(void) {

	if(!usbModules) 
	{
		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);

		retVal = sceUsbStart(PSP_USBBUS_DRIVERNAME, 0, 0);
		if (retVal != 0) {
			printf("Error starting USB Bus driver\n");
			sceKernelSleepThread();
		}
		retVal = sceUsbStart(PSP_USBSTOR_DRIVERNAME, 0, 0);
		if (retVal != 0) {
			printf("Error starting USB Mass Storage driver\n");
			sceKernelSleepThread();
		}
		retVal = sceUsbstorBootSetCapacity(0x800000);
		if (retVal != 0) {
			printf("Error setting capacity with USB Mass Storage driver\n");
			sceKernelSleepThread();
		}
		retVal = 0;
		sceKernelDelayThread(5*10000);
		usbModules=1;
	}

	if (state & PSP_USB_ACTIVATED) {
		retVal = sceUsbDeactivate(0x1c8);
		if(!firstBoot) showUsbMassStop=1;
	}
	else {
		retVal = sceUsbActivate(0x1c8);
		if(!firstBoot) showUsbMassStart=1;
	}
	return 0;
}

void unloadUsb() {

	sceUsbDeactivate(0x1c8);
	sceUsbStop(PSP_USBSTOR_DRIVERNAME, 0, 0);
  	sceUsbStop(PSP_USBBUS_DRIVERNAME, 0, 0);
}


void cleanBeforeExit() {

	SceUID thids[50];
	int thid_count = 0;
	int i;
	SceKernelThreadInfo thinfo;

	sceKernelGetThreadmanIdList(SCE_KERNEL_TMID_Thread,thids,50,&thid_count);
	for(i=0;i<thid_count;i++)
        {
		memset(&thinfo,0,sizeof(SceKernelThreadInfo));
		thinfo.size = sizeof(SceKernelThreadInfo);
            
		sceKernelReferThreadStatus(thids[i],&thinfo);

		if(strcmp(&thinfo.name[0],"SceKernelLoadExecThread")==0)
		{
			if(usbModules) unloadUsb();
			sceKernelTerminateDeleteThread(main_thid);
			sceKernelTerminateDeleteThread(display_thid);
			scePowerSetCpuClockFrequency(222);//reset to default speed
			scePowerSetBusClockFrequency(111);
			sceKernelSelfStopUnloadModule(0, 0, NULL); 
		}
	}

}

void checkUnloadBrowser() {

	SceUID thids[50];
	int thid_count = 0;
	int i;
	SceKernelThreadInfo thinfo;

	sceKernelGetThreadmanIdList(SCE_KERNEL_TMID_Thread,thids,50,&thid_count);
	for(i=0;i<thid_count;i++)
        {
		memset(&thinfo,0,sizeof(SceKernelThreadInfo));
		thinfo.size = sizeof(SceKernelThreadInfo);

		sceKernelReferThreadStatus(thids[i],&thinfo);

		if(strcmp(&thinfo.name[0],"SceHtmlViewer")==0)
		{
			showWebBrowserLoaded=1;
			//sceKernelTerminateDeleteThread(thids[i]);
		}
		/*if(strcmp(&thinfo.name[0],"SceNetCallout")==0)
		{
			sceKernelTerminateDeleteThread(thids[i]);
		}
		if(strcmp(&thinfo.name[0],"SceNetNetintr")==0)
		{
			sceKernelTerminateDeleteThread(thids[i]);
		}
		if(strcmp(&thinfo.name[0],"SceNetApctl")==0)
		{
			sceKernelTerminateDeleteThread(thids[i]);
		}
		if(strcmp(&thinfo.name[0],"SceHtmlViewerNetfront")==0)
		{
			sceKernelTerminateDeleteThread(thids[i]);
		}

		if(strcmp(&thinfo.name[0],"sceHtmlViewerHttp")==0)
		{

			sceKernelTerminateDeleteThread(thids[i]);

			unloadStopModule("sceHttpStorage_Service");
			unloadStopModule("sceSsl_Module");
			unloadStopModule("sceCert_Loader");
			unloadStopModule("sceDNAS_Library");
			unloadStopModule("sceDNASCore_lib");
			unloadStopModule("sceMcctrl");
			unloadStopModule("SceHttp_Library");
			unloadStopModule("SceParseURI_Library");
			unloadStopModule("SceParseHTTPheader_Library");
			//unloadStopModule("sceNetResolver_Library");
			//unloadStopModule("sceNetApctl_Library");
			//unloadStopModule("sceNetInet_Library");
			//unloadStopModule("sceNet_Library");
			//unloadStopModule("sceNetIfhandle_Service");
			unloadStopModule("sceHVAuth_Library");
			unloadStopModule("sceFont_Library_HV");
			unloadStopModule("sceHVSlim_Library");
			//unloadStopModule("sceHVUI_Module");
			unloadStopModule("sceHVWWW_Library");
			unloadStopModule("sceHVFlash_Module");
			unloadStopModule("sceHVNetfront_Module");
			//unloadStopModule("htmlviewer_plugin_module");
			
		}*/
	}
}

int display_thread(SceSize args, void *argp)
{
	while(1) {

		if(showHelp) {
			blit_string(10,30,"=============================",0xffffff,0x000000);
			sprintf(strBuf,"Combo Button : %i",config.comboButton);
			blit_string(10,31,strBuf,0xffffff,0x000000);
		}


		if(showCpu) {
			sprintf(strBuf,"Cpu/Bus Speed => %03d/%03d",scePowerGetCpuClockFrequency(),scePowerGetBusClockFrequency());
			blit_string(1,33,strBuf,0xffffff,0x000000);
			showCpu++;
			if(showCpu==800) showCpu=0;
		}

		if(showCaputreVideo) {
			blit_string(1,33,"Capturing Video",0xffffff,0x000000);
			showCaputreVideo++;
			if(showCaputreVideo==800) showCaputreVideo=0;
		}

		if(showDebug) {
			sprintf(strBuf,"Button : %i",config.comboButton);
			blit_string(1,33,strBuf,0xffffff,0x000000);
			showDebug++;
			if(showDebug==800) showDebug=0;
		}

		if(showUsbMassStart) {
			blit_string(1,33,"Usb Mass Storage Started",0xffffff,0x000000);
			showUsbMassStart++;
			if(showUsbMassStart==800) showUsbMassStart=0;
		}

		if(showUsbMassStop) {
			blit_string(1,33,"Usb Mass Storage Stopped",0xffffff,0x000000);
			showUsbMassStop++;
			if(showUsbMassStop==800) showUsbMassStop=0;
		}
		
		if(showScreenshot) {
			sprintf(strBuf,"Screenshot saved to : %s",config.captureFolder);
			blit_string(1,33,strBuf,0xffffff,0x000000);
			showScreenshot++;
			if(showScreenshot==800) showScreenshot=0;
		}
		
		if(showWebBrowserLoaded) 
		{
			blit_string(1,33,"You cant launch ftpd after web browser",0xffffff,0x000000);
			showWebBrowserLoaded++;
			if(showWebBrowserLoaded==800) showWebBrowserLoaded=0;
		}

	sceKernelDelayThread(1000);
	}
}


int main_thread(SceSize args, void *argp)
{

	unsigned int paddata_old = 0;
	SceCtrlData paddata;
	
	firstBoot = 1;
	state = 0;
	
	read_config("ms0:/seplugins/cfe.cfg", &config);

	if(config.enableUsbMass) {
		sceKernelDelayThread(10000);
		loadUsb();
	}

	if(config.defaultCpuSpeed) {
			sceKernelDelayThread(10000);
			cpuSpeed=config.defaultCpuSpeed;
			setCpuSpeed();
	}
	else {
		cpuSpeed=222;
	}
	
	firstBoot = 0;

	// Enter main loop
	while(1){

		sceKernelDelayThread(5*10000);

		// Read Buttons
		sceCtrlPeekBufferPositive(&paddata, 1);
		
		// Scan threads to exit properly when launching games
		cleanBeforeExit();

		// Get usb state
		state = sceUsbGetState();

		// Prevent Sleep Mode if ftpd server is running
		if(stopSleepMode) scePowerTick(0);

		// Wait for a button to be pressed
		if(paddata.Buttons != paddata_old){

			if(config.captureEnable) {
				if((paddata.Buttons & (config.captureButton | config.comboButton)) == (config.captureButton | config.comboButton)){
					showCaputreVideo=1;
					loadStartModule("ms0:/seplugins/usbhostfs.prx", 0, NULL);
					loadStartModule("ms0:/seplugins/remotejoy.prx", 0, NULL);					
				}
			}

			/*if((paddata.Buttons & (PSP_CTRL_DOWN | config.comboButton)) == (PSP_CTRL_DOWN | config.comboButton)){
				if(showHelp) {
					showHelp=0;
					sceKernelDelayThread(1000);
					//nkThreadResume(sceKernelGetThreadId());			
				} else { 
					showHelp=1;
					sceKernelDelayThread(10000);
					//nkThreadSuspend(sceKernelGetThreadId());
				}
			}*/

			if(config.poweroffEnable) 
			{
				if((paddata.Buttons & (config.poweroffButton | config.comboButton)) == (config.poweroffButton | config.comboButton))
				{
					scePowerRequestStandby();
				}
			}
			
			if(config.resetEnable) 
			{			
				if((paddata.Buttons & (config.resetButton | config.comboButton)) == (config.resetButton | config.comboButton))
				{
					sceSysconResetDevice(1,1);
				}
			}

			/*if((paddata.Buttons & (PSP_CTRL_LEFT | config.comboButton)) == (PSP_CTRL_LEFT | config.comboButton)){

				//remapUsbhostfs();
			}*/

			if(config.cpuspeedEnable) {
				if((paddata.Buttons & (config.cpuspeedButton | config.comboButton)) == (config.cpuspeedButton | config.comboButton)){
					// Change Cpu Speed
					setCpuSpeed();
				}
			}
	
			if(config.startStopUsbmassEnable) {
				if((paddata.Buttons & (config.startStopUsbmassButton | config.comboButton)) == (config.startStopUsbmassButton | config.comboButton)){
					// Load and unload usb mass storage
					loadUsb();
				}
			}
			
			if(config.takeScreenshotEnable) 
			{
				if((paddata.Buttons & (config.takeScreenshotButton | config.comboButton)) == (config.takeScreenshotButton | config.comboButton))
				{
					showScreenshot=1;
					screenshot();
				}
			}
			
			if(config.startFtpdEnable) 
			{
				if((paddata.Buttons & (config.startFtpdButton | config.comboButton)) == (config.startFtpdButton | config.comboButton))
				{
					// Load and start ftpd server module and wait 
					checkUnloadBrowser();
					sceKernelDelayThread(5*10000);
					if(!showWebBrowserLoaded) loadStartModule("ms0:/seplugins/cfe_ftpd.prx", 0, NULL);
					stopSleepMode=1;
				}
				// Resume Sleep Mode if ftpd exited
				if(paddata.Buttons & PSP_CTRL_CIRCLE) stopSleepMode=0;	
			}


		}
		paddata_old = paddata.Buttons;
		sceKernelDelayThread(10000);
	}
	return 0;
}

/* Entry point */
int module_start(SceSize args, void *argp)
{
	nkLoad();

	// Start display thread
	display_thid = sceKernelCreateThread("cfe_vsh_display", display_thread, 8, 0x04000, 0, NULL);
	if(display_thid >= 0) sceKernelStartThread(display_thid, args, argp);

	sceKernelDelayThread(10000);

	main_thid = sceKernelCreateThread("cfe_vsh_main", main_thread, 6, 0x04000, 0, NULL);
	if(main_thid >= 0) sceKernelStartThread(main_thid, args, argp);

	sceKernelExitDeleteThread(0);
	
	return 0;
}

/* Module stop entry */
int module_stop(SceSize args, void *argp)
{
	return 0;
}
