Esempio n. 1
0
s32 Title_GetTMD(u64 tid, signed_blob **outbuf, u32 *outlen)
{
	void *p_tmd = NULL;

	u32 len;
	s32 ret;

	/* Get TMD size */
	ret = ES_GetStoredTMDSize(tid, &len);
	if (ret < 0)
		return ret;

	/* Allocate memory */
	p_tmd = memalign(32, round_up(len, 32));
	if (!p_tmd)
		return -1;

	/* Read TMD */
	ret = ES_GetStoredTMD(tid, p_tmd, len);
	if (ret < 0)
		goto err;

	/* Set values */
	*outbuf = p_tmd;
	*outlen = len;

	return 0;

err:
	/* Free memory */
	if (p_tmd)
		free(p_tmd);

	return ret;
}
Esempio n. 2
0
s32 GetTMD(u64 TicketID, signed_blob **Output, u32 *Length)
{
    signed_blob* TMD = NULL;

    u32 TMD_Length;
    s32 ret;

    /* Retrieve TMD length */
    ret = ES_GetStoredTMDSize(TicketID, &TMD_Length);
    if (ret < 0)
        return ret;

    /* Allocate memory */
    TMD = (signed_blob*)MEM2_memalign(32, (TMD_Length+31)&(~31));
    if (!TMD)
        return IPC_ENOMEM;

    /* Retrieve TMD */
    ret = ES_GetStoredTMD(TicketID, TMD, TMD_Length);
    if (ret < 0)
    {
        free(TMD);
        return ret;
    }

    /* Set values */
    *Output = TMD;
    *Length = TMD_Length;

    return 0;
}
Esempio n. 3
0
signed_blob *GetTMD(u8 ios, u32 *TMD_Length)
{
	if(ES_GetStoredTMDSize(TITLE_ID(1, ios), TMD_Length) < 0)
		return NULL;

	signed_blob *TMD = (signed_blob*)MEM2_alloc(*TMD_Length);
	if(TMD == NULL)
		return NULL;
	if(ES_GetStoredTMD(TITLE_ID(1, ios), TMD, *TMD_Length) < 0)
	{
		MEM2_free(TMD);
		return NULL;
	}
	return TMD;
}
Esempio n. 4
0
bool isIOSstub(u8 ios_number)
{
	static signed_blob ios_tmd_buf[MAX_SIGNED_TMD_SIZE] ATTRIBUTE_ALIGN(32);
	u32 tmd_size;
	tmd *ios_tmd;
	
	if (ios_number == 249)
	{
		ES_GetStoredTMDSize(0x0000000100000000ULL | ios_number, &tmd_size);
		ES_GetStoredTMD(0x0000000100000000ULL | ios_number, ios_tmd_buf, tmd_size);
		ios_tmd = SIGNATURE_PAYLOAD(ios_tmd_buf);
		if (ios_tmd->title_version >= 65280) return TRUE;
	}
	
	return FALSE;
}
tmd* NandTitle::GetTMD(u64 tid)
{
	signed_blob *s_tmd = (signed_blob *) tmd_buf;
	u32 tmd_size;

	if (ES_GetStoredTMDSize(tid, &tmd_size) < 0)
	{
		return NULL;
	}

	s32 ret = ES_GetStoredTMD(tid, s_tmd, tmd_size);
	if (ret < 0)
	{
		return NULL;
	}

	tmd *t = (tmd*) SIGNATURE_PAYLOAD(s_tmd);

	return t;
}
Esempio n. 6
0
s32 WII_OpenURL(const char *url)
{
	u32 tmdsize;
	u64 tid = 0;
	u64 *list;
	u32 titlecount;
	s32 ret;
	int i;

	if(!__initialized)
		return WII_ENOTINIT;

	ret = ES_GetNumTitles(&titlecount);
	if(ret < 0)
		return WII_EINTERNAL;

	list = memalign(32, titlecount * sizeof(u64) + 32);

	ret = ES_GetTitles(list, titlecount);
	if(ret < 0) {
		free(list);
		return WII_EINTERNAL;
	}

	for(i=0; i<titlecount; i++) {
		if((list[i] & ~0xFF) == 0x1000148414400LL) {
			tid = list[i];
			break;
		}
	}
	free(list);

	if(!tid)
		return WII_EINSTALL;

	if(ES_GetStoredTMDSize(tid, &tmdsize) < 0)
		return WII_EINSTALL;

	return WII_LaunchTitleWithArgs(tid, 0, url, NULL);
}
Esempio n. 7
0
//boot HBC in either HAXX or JODI locations
//this function expects WII_Initialize() be called before it is called
s32 WII_BootHBC()
{
	u32 tmdsize;
	u64 tid = 0;
	u64 *list;
	u32 titlecount;
	s32 ret;
	u32 i;

	ret = ES_GetNumTitles(&titlecount);
	if(ret < 0)
		return WII_EINTERNAL;

	list = CFMemAlign(32, titlecount * sizeof(u64) + 32);

	ret = ES_GetTitles(list, titlecount);
	if(ret < 0) {
		CFFree(list);
		return WII_EINTERNAL;
	}
	
	for(i=0; i<titlecount; i++) {
		if (list[i]==TITLE_ID(0x00010001,0x4A4F4449) || list[i]==TITLE_ID(0x00010001,0x48415858) || list[i]==TITLE_ID(0x00010001,0xaf1bf516))
		{
			tid = list[i];
			break;
		}
	}
	CFFree(list);

	if(!tid)
		return WII_EINSTALL;

	if(ES_GetStoredTMDSize(tid, &tmdsize) < 0)
		return WII_EINSTALL;

	return WII_LaunchTitle(tid);
}
Esempio n. 8
0
s32 LoadKernel()
{
	u32 TMDSize;
	u32 i,u;

	s32 r = ES_GetStoredTMDSize( 0x000000010000003aLL, &TMDSize );
	if( r < 0 )
	{
		gprintf("ES_GetStoredTMDSize():%d\r\n", r );
		return r;
	}

	gprintf("TMDSize:%u\r\n", TMDSize );

	TitleMetaData *TMD = (TitleMetaData*)memalign( 32, TMDSize );
	if( TMD == (TitleMetaData*)NULL )
	{
		gprintf("Failed to alloc:%u\r\n", TMDSize );
		return r;	//todo errors are < 0 r still has >= 0 from previous call
	}

	r = ES_GetStoredTMD( 0x000000010000003aLL, (signed_blob *)TMD, TMDSize );
	if( r < 0 )
	{
		gprintf("ES_GetStoredTMD():%d\r\n", r );
		free(TMD);
		return r;
	}

//Look for boot index
	for( i=0; i < TMD->ContentCount; ++i )
	{
		if( TMD->BootIndex == TMD->Contents[i].Index )
			break;
	}
	
	gprintf("BootIndex:%u\r\n", i );

	s32 cfd = IOS_Open( "/shared1/content.map", 1 );
	if( cfd < 0 )
	{
		gprintf("IOS_Open():%d\r\n", cfd );
		free(TMD);
		return cfd;
	}

	for( u=0;; u+=0x1C )
	{
		if( IOS_Read( cfd, Entry, 0x1C ) != 0x1C )
		{
			gprintf("Hash not found in content.map\r\n");
			free(TMD);
			return -2;
		}

		if( memcmp( (char*)(Entry+8), TMD->Contents[i].SHA1, 0x14 ) == 0 )
			break;
	}
	FoundVersion = ((TMD->TitleID & 0xFFFF) << 16) | (TMD->TitleVersion);
	free(TMD);

	IOS_Close( cfd );

	u32 *IOSVersion = (u32*)0x93003000;
	DCInvalidateRange(IOSVersion, 0x20);
	*IOSVersion = FoundVersion;
	gprintf("IOS Version: 0x%08X\n", FoundVersion);
	DCFlushRange(IOSVersion, 0x20);

	//char Path[32];
	char *Path = (char*)0x93003020;
	DCInvalidateRange(Path, 1024);
	memset(Path, 0, 1024);
	sprintf( Path, "/shared1/%.8s.app", Entry );
	gprintf("Kernel:\"%s\"\r\n", Path );
	DCFlushRange(Path, 1024);

	s32 kfd = IOS_Open( Path, 1 );
	if( kfd < 0 )
	{
		gprintf("IOS_Open():%d\r\n", kfd );
		return kfd;
	}

	KernelSize = IOS_Seek( kfd, 0, SEEK_END );
	IOS_Seek( kfd, 0, 0);

	gprintf("KernelSize:%u\r\n", KernelSize );

	if( IOS_Read( kfd, Kernel, KernelSize ) != KernelSize )
	{
		gprintf("IOS_Read() failed\r\n");

		IOS_Close(kfd);
		return -1;
	}
	IOS_Close(kfd);

	return 0;
}
Esempio n. 9
0
s32 *get_ioslist(u32 *cnt)
{
	u64 *buf = 0;
	s32 res;
	u32 i, k = 0, tcnt = 0, icnt = 0;
	s32 *ioses = NULL;
	
	bool skip_title;
	
	/* Get stored IOS versions */
	res = ES_GetNumTitles(&tcnt);
	if (res < 0)
	{
		printf("\t- ES_GetNumTitles: Error! (result = %ld).\n", res);
		return 0;
	}
	
	buf = memalign(32, sizeof(u64) * tcnt);
	if (!buf) 
	{
		printf("\t- Error allocating titlelist memory buffer!\n");
		return 0;
	}
	
	res = ES_GetTitles(buf, tcnt);
	if (res < 0)
	{
		printf("\t- ES_GetTitles: Error! (result = %ld).\n", res);
		free(buf);
		return 0;
	}
	
	for (i = 0; i < tcnt; i++)
	{
		/* Skip BC, MIOS, System Menu, BootMii IOS, BC-NAND, BC-WFS and stub IOSses */
		if ((TITLE_UPPER(buf[i - k]) == 1) && (TITLE_LOWER(buf[i - k]) > 2) && (TITLE_LOWER(buf[i - k]) < 0xFE))
		{
			u32 tmdSize = 0;
			tmd *iosTMD = NULL;
			signed_blob *iosTMDBuffer = NULL;
			
			/* Get stored TMD size */
			res = ES_GetStoredTMDSize(buf[i - k], &tmdSize);
			if (res < 0)
			{
				printf("\t- ES_GetStoredTMDSize: Error! (result = %ld / IOS%lu).\n", res, ((u32)buf[i - k]));
				break;
			}
			
			iosTMDBuffer = (signed_blob*)memalign(32, (tmdSize+31)&(~31));
			if (!iosTMDBuffer)
			{
				res = -1;
				printf("\t- Error allocating IOS%lu TMD buffer (size = %lu bytes).\n", ((u32)buf[i - k]), tmdSize);
				break;
			}
			
			memset(iosTMDBuffer, 0, tmdSize);
			
			/* Get stored TMD */
			res = ES_GetStoredTMD(buf[i - k], iosTMDBuffer, tmdSize);
			if (res < 0)
			{
				printf("\t- ES_GetStoredTMD: Error! (result = %ld / IOS%lu).\n", res, ((u32)buf[i - k]));
				free(iosTMDBuffer);
				break;
			}
			
			iosTMD = (tmd*)SIGNATURE_PAYLOAD(iosTMDBuffer);
			
			/* Calculate title size */
			u16 j;
			u32 titleSize = 0;
			for (j = 0; j < iosTMD->num_contents; j++)
			{
				tmd_content *content = &iosTMD->contents[j];
				
				/* Add content size */
				titleSize += content->size;
			}
			
			/* Check if this IOS is a stub */
			skip_title = (titleSize < 0x100000);
			
			free(iosTMDBuffer);
		} else {
			skip_title = true;
		}
		
		if (!skip_title)
		{
			icnt++;
		} else {
			/* Move around memory blocks */
			if ((tcnt - 1) > i)
			{
				memmove(&(buf[i - k]), &(buf[i - k + 1]), (sizeof(u64) * (tcnt - i - 1)));
				k++;
			}
		}
	}
	
	if (res < 0)
	{
		free(buf);
		return 0;
	}
	
	if (realloc(buf, sizeof(u64) * icnt) == NULL)
	{
		printf("\t- Error reallocating titlelist memory block!\n");
		free(buf);
		return 0;
	}
	
	ioses = (s32 *)malloc(sizeof(s32) * icnt);
	if (!ioses)
	{
		printf("\t- Error allocating IOS memory buffer!\n");
		free(buf);
		return 0;
	}
	
	for (i = 0; i < icnt; i++) ioses[i] = (s32)buf[i];
	
	free(buf);
	qsort(ioses, icnt, sizeof(s32), __s32Cmp);
	*cnt = icnt;
	
	return ioses;
}
Esempio n. 10
0
s32 extractChannelContents(u64 titleID, char* location)
{
	u32 TitleIDH=TITLE_UPPER(titleID);
	u32 TitleIDL=TITLE_LOWER(titleID);
	if(ES_SetUID(titleID)<0)
		return ERR_UID;

	u32 tmd_size ATTRIBUTE_ALIGN(32);


	if(ES_GetStoredTMDSize(titleID, &tmd_size)<0)
		return ERR_TMDSIZE;

	signed_blob *TMD = (signed_blob *)memalign( 32, tmd_size );
	memset(TMD, 0, tmd_size);

	if(ES_GetStoredTMD(titleID, TMD, tmd_size)<0)
	{
		free(TMD);
		return ERR_TMD;
	}

	u32 cnt ATTRIBUTE_ALIGN(32);

	if(ES_GetNumTicketViews(titleID, &cnt)<0)
	{
		free(TMD);
		return ERR_TIKCOUNT;
	}

	if( cnt <= 0 )
	{
		free(TMD);
		return ERR_TIKCOUNT;
	}

	tikview *views = (tikview *)memalign( 32, sizeof(tikview)*cnt );
	if(ES_GetTicketViews(titleID, views, cnt)<0)
	{
		free(views);
		free(TMD);
		return ERR_TIK;
	}
	printf("Allocated and filled views.\n");
	sleep(3);

	Identify_SU();

	int z;
	tmd_content *TMDc = TMD_CONTENTS(((tmd*)(SIGNATURE_PAYLOAD(TMD))));		// OH GOD CREDIAR, WTF WAS THAT MESS!
	// List format is "XXXXXXXX = YYYYYYYY" where X is index, and Y is cid.
	char *lookup_list=calloc(21,((tmd*)(SIGNATURE_PAYLOAD(TMD)))->num_contents);
	ClearScreen();
	printf("\nNumber of contents: %d\n",((tmd*)(SIGNATURE_PAYLOAD(TMD)))->num_contents);
	sleep(1);
	for(z=0; z < ((tmd*)(SIGNATURE_PAYLOAD(TMD)))->num_contents; z++)
	{
		/* Get Content */
		char nameDirectory[80];
		sprintf(nameDirectory,"/title/%08x/%08x/content/%08x.app",TitleIDH,TitleIDL,TMDc[z].cid);
		s32 contentFd=ISFS_Open(nameDirectory,ISFS_OPEN_READ);
		u8 *data=calloc(TMDc[z].size,1);
		if(contentFd<0)
		{
			switch(contentFd)
			{
				case ISFS_EINVAL:
					printf("FAILED! (Invalid Argument %s)\n\tQuitting...\n",nameDirectory);
					sleep(5);
					Finish(1);
					break;
			
				case ISFS_ENOMEM:
					printf("FAILED! (Out of memory %s)\n\tQuitting...\n",nameDirectory);
					sleep(5);
					Finish(1);
					break;
			
				default:
					goto skip;
//					Finish(1);
					break;
			}
		}
		int isUnaligned = ((int)data)%32 | TMDc[z].size%32;
		int alignedLen = (TMDc[z].size+31)&0xffffffe0;
		unsigned char* alignedBuf;
		if(isUnaligned) alignedBuf = memalign(32, alignedLen);
		else alignedBuf = data;
		ISFS_Seek(contentFd,0,0);
		ISFS_Read(contentFd, alignedBuf, alignedLen);
		// If it was unaligned, you have to copy it and clean up
		if(isUnaligned){
			memcpy(data, alignedBuf, TMDc[z].size);
			free(alignedBuf);
		}
		ISFS_Close(contentFd);

		// Do copying here.
		// data is the actual content data (use fwrite with it :P).
		// Copy the file with it's index as it's filename
		char* destination=calloc(sizeof(location)+14, 1);
		char lookup_entry[21];
		sprintf(destination, "%s/%08x.app",location,TMDc[z].index);
		sprintf(lookup_entry, "%08x = %08x\n",TMDc[z].index,TMDc[z].cid);
		strcat(lookup_list, lookup_entry);
		printf("Got destination as: %s\n", destination);
		sleep(3);
		FILE *dfd=fopen(destination,"wb+");
		printf("Opened %s\n", destination);
		sleep(3);
//		free(destination);
		fwrite(data, TMDc[z].size, 1, dfd);
		printf("Wrote data to %s\n", destination);
		sleep(3);
		fclose(dfd);
		printf("Closed %s\n", destination);
		sleep(2);
skip:
		_nop();
	}
	// Make a file containing the lookups called files.txt
	char* lookup_file=calloc(sizeof(location)+14, 1);
	sprintf(lookup_file, "%s/files.txt",location);
	printf("Got destination as: %s\n", lookup_file);
	sleep(3);
	FILE* lfd=fopen(lookup_file,"wb+");
	printf("Opened %s\n", lookup_file);
	sleep(3);
//	free(lookup_file);
	fwrite(lookup_list, 21, ((tmd*)SIGNATURE_PAYLOAD(TMD))->num_contents, lfd);
	printf("Wrote lookups to %s\n", lookup_file);
	sleep(3);
	fclose(lfd);
	printf("Closed %s\n", lookup_file);
	sleep(2);
	printf("Freed TMD and views");
	sleep(1);
Esempio n. 11
0
	return ERR_NONE;
}

void changeChannelName(u64 titleID)
{
	printf("Identifying as 00000001-00000000 (SU)!... ");
	CheckESRetval(ES_Identify(SU_IDENTIFY));
//	if(ES_SetUID(titleID)<0)
//		return;

	u32 tmd_size ATTRIBUTE_ALIGN(32);


	if(CheckESRetval(ES_GetStoredTMDSize(titleID, &tmd_size))!=0)
		return;

	signed_blob *TMD = (signed_blob *)memalign( 32, tmd_size );
	memset(TMD, 0, tmd_size);

	if(CheckESRetval(ES_GetStoredTMD(titleID, TMD, tmd_size))!=0)
	{
		free(TMD);
		return;
	}

	u32 cnt ATTRIBUTE_ALIGN(32);

	if(CheckESRetval(ES_GetNumTicketViews(titleID, &cnt))!=0)
	{
		free(TMD);
		return;
	}

	if( cnt <= 0 )
	{
		free(TMD);
		return;
	}

	tikview *views = (tikview *)memalign( 32, sizeof(tikview)*cnt );

	if(CheckESRetval(ES_GetTicketViews(titleID, views, cnt))!=0)
	{
		free(views);
		free(TMD);
		return;
	}
	char *name=calloc((0x54/2),1);
	int z;
	for(z=0; z < 1; ++z)
	{
		tmd_content *TMDc = TMD_CONTENTS(((tmd*)(SIGNATURE_PAYLOAD(TMD))));		// OH GOD CREDIAR, WTF WAS THAT MESS!!!
		//printf("%d,",TMDc->index);
		s32 cfd = ES_OpenTitleContent( titleID, TMDc->index);
		free(views);
		if(CheckESRetval(cfd)!=0)
		{	
			;
			//printf("ES_OpenContent(%d) failed\n", cfd);
			//sleep(10);
			//exit(0);
		} else {
			u64 sz ATTRIBUTE_ALIGN(32) = 0x140;
			u8 *data = (u8*)memalign(32, sz);

			if( TMDc->size < sz )
				sz = TMDc->size;

			if( data != NULL )
			{
				if(ES_ReadContent(cfd, data, sz)<0)
				{
					free(data);
					return;
				}

				int y;
				int chan_name_offset=(language_setting*0x54);				// Set to WiiMU's language
				for(y=0;y<(0x54/2);y++)
					name[y]=data[(0x9C+chan_name_offset)+(y*2)+1];

			}
			if(CheckESRetval(ES_CloseContent(cfd))!=0)
			{
				;
				//printf("ES_CloseContent failed\n");
				//sleep(10);
				//exit(0);
			}
			free(data);
		}
	}
	int wiistring_size;
reenter:
	wiistring_size=type_string_wiimote(name, 0x54/2);
	if(wiistring_size<=0)
	{
		printf("\n\nPlease enter a name!\n");
		sleep(3);
		goto reenter;
	}
	name[wiistring_size+1]=0;

	/* Get Content */
	char nameDirectory[80];
	tmd_content *TMDc = TMD_CONTENTS(((tmd*)(SIGNATURE_PAYLOAD(TMD))));
	sprintf(nameDirectory,"/title/%08x/%08x/content/%08x.app",TITLE_UPPER(titleID),TITLE_LOWER(titleID),TMDc->cid);
	s32 contentFd=ISFS_Open(nameDirectory,ISFS_OPEN_RW);
	CheckISFSRetval(contentFd);
	ClearScreen();
	printf("\n\nOpened content!\n");
	u8 *data=calloc(TMDc->size,1);
	int isUnaligned = ((int)data)%32 | TMDc->size%32;
	int alignedLen = (TMDc->size+31)&0xffffffe0;
	u8* alignedBuf;
	if(isUnaligned) alignedBuf = memalign(32, alignedLen);
	else alignedBuf = data;
	CheckISFSRetval(ISFS_Seek(contentFd,0,0));
	CheckISFSRetval(ISFS_Read(contentFd, alignedBuf, alignedLen));
	printf("Read content!\n");
	int y;
	int chan_name_offset=(SYSCONF_GetArea()*0x54);		// Edits the one for the Wii's system Menu
	char* nameOut=calloc(96,1);
	for(y=0;y<(0x54/2);y++)
		nameOut[(y*2)+1]=name[y];
	printf("Wrote new name! %s\n", name);
	CheckISFSRetval(ISFS_Seek(contentFd,(0x9C+chan_name_offset),0));
	printf("Seeked to location!\n");
	if(nameOut==NULL)
	{
		printf("FAILED! (Name Out is NULL!)\n");
		sleep(5);
		Finish(1);
	}
	if(((u32)nameOut%32)!=0)
	{
		isUnaligned = ((int)nameOut)%32 | 0x54%32;
		alignedLen = (0x54+31)&0xffffffe0;
		if(isUnaligned){ alignedBuf = memalign(32, alignedLen); memcpy(alignedBuf, nameOut, 0x54); }
		else alignedBuf = (u8*)nameOut;
	}
	CheckISFSRetval(ISFS_Write(contentFd, alignedBuf, 0x54));
	printf("Wrote content name!\nReading new Header Chunk!\n");
	CheckISFSRetval(ISFS_Seek(contentFd,0,0));
	CheckISFSRetval(ISFS_Read(contentFd, alignedBuf, alignedLen));
	printf("Read content!\n");
	u8* header_chunk=calloc(0x640, 1);
	int i;
	for(i=0;i<0x630;i++)
		header_chunk[i]=alignedBuf[i];
	for(i=0x630;i<0x640;i++)
		header_chunk[i]=0;
	u8* hash=calloc(0x10,1);
	md5(header_chunk, 0x640, hash);
	CheckISFSRetval(ISFS_Seek(contentFd,0x630,0));
	printf("Seeked to location!\n");
	if(hash==NULL)
	{
		printf("FAILED! (Hash is NULL!)\n");
		sleep(5);
		Finish(1);
	}
	if(((u32)hash%32)!=0)
	{
		isUnaligned = ((int)hash)%32 | 0x10%32;
		alignedLen = (0x10+31)&0xffffffe0;
		if(isUnaligned){ alignedBuf = memalign(32, alignedLen); memcpy(alignedBuf, hash, 0x10); }
		else alignedBuf = hash;
Esempio n. 12
0
s32 IOS_GetPreferredVersion()
{
	int ver = IOS_EBADVERSION;
	s32 res;
	u32 count;
	u64 *titles;
	u32 tmd_size;
	u32 i;
	u32 a,b;

	res = __IOS_InitHeap();
	if(res<0) return res;

	res = ES_GetNumTitles(&count);
	if(res < 0) {
#ifdef DEBUG_IOS
		printf(" GetNumTitles failed: %d\n",res);
#endif
		return res;
	}
#ifdef DEBUG_IOS
	printf(" %d titles on card:\n",count);
#endif
	titles = iosAlloc(__ios_hid, sizeof(u64)*count);
	if(!titles) {
		printf(" iosAlloc titles failed\n");
		return -1;
	}
	res = ES_GetTitles(titles, count);
	if(res < 0) {
#ifdef DEBUG_IOS
		printf(" GetTitles failed: %d\n",res);
#endif
		iosFree(__ios_hid, titles);
		return res;
	}
	
	u32 *tmdbuffer = memalign(32, MAX_SIGNED_TMD_SIZE);

	if(!tmdbuffer)
	{
		iosFree(__ios_hid, titles);
		return -1;
	}

	for(i=0; i<count; i++) {
		a = titles[i]>>32;
		b = titles[i]&0xFFFFFFFF;
		if(a != 1) continue;
		if(b < IOS_MIN_VERSION) continue;
		if(b > IOS_MAX_VERSION) continue;
		
		if (ES_GetStoredTMDSize(titles[i], &tmd_size) < 0)
			continue;

		if (tmd_size < 0 || tmd_size > 4096)
			continue;

		if(ES_GetStoredTMD(titles[i], (signed_blob *)tmdbuffer, tmd_size) < 0)
			continue;

		if (!tmdbuffer[1] && !tmdbuffer[2])
			continue;
		
		if((((s32)b) > ((s32)ver) && ver != 58) || b == 58) ver = b;
	}
#ifdef DEBUG_IOS
	printf(" Preferred verson: %d\n",ver);
#endif
	iosFree(__ios_hid, titles);
	free(tmdbuffer);
	return ver;
}