/*
 *  Copyright (C) 2008 Nuke (wiinuke@gmail.com)
 *
 *  this file is part of GeckoOS for USB Gecko
 *  http://www.usbgecko.com
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <ogc/es.h>
#include <ogcsys.h>
#include <ogcsys.h>
#include <sys/unistd.h>
#include "apploader.h"
#include "patchcode.h"
#include "tools.h"
#include "geckomenu.h"
#include "font.h"
#include "identify.h"
#include "menu_tik_bin.h"
#include "menu_tmd_bin.h"
#include "menu_certs_bin.h"
#include "sysmenu.h"
#include "multidol.h"


extern u32 *xfb;
extern const unsigned char kenobiwii[];
extern const int kenobiwii_size;
void _unstub_start();
//static const char certs_fs[] ATTRIBUTE_ALIGN(32) = "/sys/cert.sys";

typedef struct _dolheader
{
	u32 text_pos[7];
	u32 data_pos[11];
	u32 text_start[7];
	u32 data_start[11];
	u32 text_size[7];
	u32 data_size[11];
	u32 bss_start;
	u32 bss_size;
	u32 entry_point;
} dolheader;

/*
s32 sys_getcerts(signed_blob **certs, u32 *len)
{
	static signed_blob certificates[0x280] ATTRIBUTE_ALIGN(32);
	s32 fd, ret;

	fd = IOS_Open(certs_fs, 1);
	if (fd < 0)
		return fd;

	ret = IOS_Read(fd, certificates, sizeof(certificates));

	IOS_Close(fd);

	if (ret > 0) {
		*certs = certificates;
		*len   = sizeof(certificates);
	}

	return ret;
}
*/

u8 iosReloadOrig[] = {
	0x7f, 0x06, 0xc3, 0x78,
	0x7f, 0x25, 0xcb, 0x78,
	0x38, 0x7e, 0x02, 0xc0,
	0x4c, 0xc6, 0x31, 0x82
};

u8 iosReloadPatch[] = {
	0x3b, 0x20, 0x00, 0x01,
	0x3b, 0x00, 0x00, 0xf9
};

void iosReloadFix(void *addr, u32 len)
{
	void *addr_start = addr;
	void *addr_end = addr+len;

	while(addr_start < addr_end)
	{
		if(memcmp(addr_start, iosReloadOrig, sizeof(iosReloadOrig))==0) {
			memcpy(addr_start, iosReloadPatch, sizeof(iosReloadPatch));
		}
		addr_start += 4;
	}
}

void boot_menu()
{
	s32 ret;
	clearscreen(xfb, 20, 80, 278, 315, 0x00800080);
	write_font(185, 154, "Rebooting With Hooks");
		
	ret = Menu_identify();
	if(!ret){
		write_font(185, 178, "Error Loading Menu");
		sleep(2);
		return;
	}
	
	ret = ES_Load_dol(bootindex);
	if(!ret){
		write_font(185, 178, "Error Loading Menu");
		sleep(2);
		return;
	}
	// Clear the Game ID
	memset((void*)0x80000000,0,6);

	// Load WiiRD handler
	memset((void*)0x80001800,0,kenobiwii_size);
	memcpy((void*)0x80001800,kenobiwii,kenobiwii_size); 
	DCFlushRange((void*)0x80001800,kenobiwii_size);
	// Load Sysmenu patch handler
	memset((void*)0x81700000,0,sysmenu_size);
	memcpy((void*)0x81700000,sysmenu,sysmenu_size); 
	DCFlushRange((void*)0x81700000,sysmenu_size);
	// Load multidol handler
	memset((void*)0x80001000,0,multidol_size);
	memcpy((void*)0x80001000,multidol,multidol_size); 
	DCFlushRange((void*)0x80001000,multidol_size);
	
	// Patches
	if(recoveryselect){
		hooktype = 4;
		dogamehooks((void*)0x8132ff80, 0x380000);	// menu size
		shutdown_all();
		_unstub_start();
	}
	// Region Free
	if(regionfreeselect){
		hooktype = 6;
		dogamehooks((void*)0x8132ff80, 0x380000);	
	}
	// Health check
	if(buttonskipselect){
		hooktype = 7;
		dogamehooks((void*)0x8132ff80, 0x380000);
	}
	// No copy bit remove
	if(nocopyselect){
		hooktype = 8;
		dogamehooks((void*)0x8132ff80, 0x380000);
	}
	
	hooktype = 5;
	dogamehooks((void*)0x8132ff80, 0x380000);

	hooktype = 10;
	dogamehooks((void*)0x8132ff80, 0x380000);
	
	// move dvd channel
	hooktype = 9;
	dogamehooks((void*)0x8132ff80, 0x380000);

	// Use IOS249 for starting games
	iosReloadFix((void*)0x8132ff80, 0x380000);

	// default multi dol and hooks

	DCFlushRange((void*)0x8132ff80, 0x380000);
	
	ocarinarebooter();
	memset((void*)0x800027E8,0,4); 
	DCFlushRange((void*)0x80000000, 0x3f00);
	shutdown_all();
	_unstub_start();
}

s32 Menu_identify()
{
	u64 titleID;
	u32 tmdSize;
	signed_blob *ptmd;
	s32 res;
	signed_blob *p_certs = NULL;
	u32 certs_len;
	
	// Get certs
//	res = sys_getcerts(&p_certs, &certs_len);
//	if (res < 0){
//		return 0;
//	}

	//Identify as menu.
	res = ES_Identify((signed_blob*)menu_certs_bin, menu_certs_bin_size, (signed_blob*)menu_tmd_bin, menu_tmd_bin_size, (signed_blob*)menu_tik_bin, menu_tik_bin_size, NULL);
	//res = ES_Identify(p_certs, certs_len, (signed_blob*)menu_tmd_bin, menu_tmd_bin_size, (signed_blob*)menu_tik_bin, menu_tik_bin_size, NULL);
	if(res < 0){
		return 0;
	}
	
	//Get current title ID.
	ES_GetTitleID(&titleID);
	if(titleID != 0x0000000100000002ULL){
		return 0;
	}
	
	//Get tmd size.
	res = ES_GetStoredTMDSize(titleID, &tmdSize);
	if(res < 0){
		return 0;
	}
	
	//Get tmd.
	ptmd = memalign(32, tmdSize);
	res = ES_GetStoredTMD(titleID, ptmd, tmdSize);
	if(res < 0){
		return 0;
	}
	
	//Identify as menu.
	res = ES_Identify((signed_blob*)menu_certs_bin, menu_certs_bin_size, ptmd, tmdSize, (signed_blob*)menu_tik_bin, menu_tik_bin_size, NULL);
	//res = ES_Identify(p_certs, certs_len, (signed_blob*)menu_tmd_bin, menu_tmd_bin_size, (signed_blob*)menu_tik_bin, menu_tik_bin_size, NULL);
	if(res < 0){
		return 0;
	}
	
	ES_GetTitleID(&titleID);
	bootindex = ((tmd *)SIGNATURE_PAYLOAD(ptmd))->boot_index;
	return 1;
}

s32 ES_Load_dol(u16 index)
{
	u32 i;
	s32 ret;

	dolheader *dolfile = (dolheader*)memalign(32, sizeof(dolheader));
	
	ret = ES_OpenContent(index);
	if(ret < 0){
		return 0;
	}
	
	ES_ReadContent(ret, (void *)dolfile, sizeof(dolheader));
	
	memset((void *)dolfile->bss_start, 0, dolfile->bss_size);
	
	for (i = 0; i < 7; i++)
	{
		if(dolfile->data_start[i] < sizeof(dolheader))
			continue;
		
		ES_SeekContent(ret, dolfile->text_pos[i], 0);
		ES_ReadContent(ret, (void *)dolfile->text_start[i], dolfile->text_size[i]);
	}
	DCFlushRange((void *)dolfile->text_start[i], dolfile->text_size[i]);

	for(i = 0; i < 11; i++)
	{
		if(dolfile->data_start[i] < sizeof(dolheader))
			continue;
				
		ES_SeekContent(ret, dolfile->data_pos[i], 0);
		ES_ReadContent(ret, (void *)dolfile->data_start[i], dolfile->data_size[i]);
	}
	DCFlushRange((void *)dolfile->data_start[i], dolfile->data_size[i]);
	
	ES_CloseContent(ret);	
	return 1;
}
