mirror of
https://github.com/wiidev/usbloadergx.git
synced 2024-11-04 18:45:05 +01:00
3f9cdb5468
*Moved R896 to the branch
627 lines
16 KiB
C++
627 lines
16 KiB
C++
#include <string.h>
|
|
|
|
#include "settings/cfg.h"
|
|
#include "usbloader/wbfs.h"
|
|
#include "main.h"
|
|
#include <wctype.h>
|
|
#include "getentries.h"
|
|
#include "settings/newtitles.h"
|
|
|
|
#include "../prompts/TitleBrowser.h"
|
|
|
|
#include "wad/wad.h"
|
|
#include "xml/xml.h"
|
|
#include "../wad/title.h"
|
|
#include <algorithm>
|
|
#include <vector>
|
|
#include <wchar.h>
|
|
|
|
#include "listfiles.h"
|
|
#define typei 0x00010001
|
|
|
|
|
|
struct discHdr * gameList=NULL;
|
|
s32 gameSelected=0, gameStart=0;
|
|
u32 gameCnt=0;
|
|
wchar_t *gameFilter=NULL;
|
|
wchar_t *gameFilterNextList=NULL;
|
|
wchar_t *gameFilterPrev=NULL;
|
|
|
|
extern u8 mountMethod;
|
|
|
|
/****************************************************************************
|
|
* wcsdup_new based on new wchar_t [...]
|
|
***************************************************************************/
|
|
static wchar_t *wcsdup_new(const wchar_t *src)
|
|
{
|
|
int len = wcslen(src)+1;
|
|
wchar_t *dst = new wchar_t[len];
|
|
if(dst) wcscpy(dst, src);
|
|
return dst;
|
|
}
|
|
static inline int wcsnicmp(const wchar_t *s1, const wchar_t *s2, int len)
|
|
{
|
|
if (len <= 0)
|
|
return (0);
|
|
do
|
|
{
|
|
int r = towupper(*s1) - towupper(*s2++);
|
|
if (r) return r;
|
|
if (*s1++ == 0)
|
|
break;
|
|
} while (--len != 0);
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* EntryCmp
|
|
***************************************************************************/
|
|
s32 __Menu_EntryCmp(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
struct discHdr *hdr1 = (struct discHdr *)a;
|
|
|
|
struct discHdr *hdr2 = (struct discHdr *)b;
|
|
|
|
|
|
|
|
/* Compare strings */
|
|
|
|
return stricmp(get_title(hdr1), get_title(hdr2));
|
|
|
|
}
|
|
|
|
s32 __Menu_EntryCmpCount(const void *a, const void *b) {
|
|
s32 ret;
|
|
|
|
struct discHdr *hdr1 = (struct discHdr *)a;
|
|
|
|
struct discHdr *hdr2 = (struct discHdr *)b;
|
|
|
|
/* Compare Play Count */
|
|
u16 count1 = 0;
|
|
u16 count2 = 0;
|
|
struct Game_NUM* game_num1 = CFG_get_game_num(hdr1->id);
|
|
struct Game_NUM* game_num2 = CFG_get_game_num(hdr2->id);
|
|
|
|
|
|
|
|
if (game_num1) count1 = game_num1->count;
|
|
if (game_num2) count2 = game_num2->count;
|
|
|
|
ret = (s32) (count2-count1);
|
|
if (ret == 0) return stricmp(get_title(hdr1), get_title(hdr2));
|
|
|
|
return ret;
|
|
}
|
|
|
|
s32 __Menu_EntryCmpFavorite(const void *a, const void *b) {
|
|
s32 ret;
|
|
|
|
struct discHdr *hdr1 = (struct discHdr *)a;
|
|
|
|
struct discHdr *hdr2 = (struct discHdr *)b;
|
|
|
|
/* Compare Favorite (rank) */
|
|
u16 fav1 = 0;
|
|
u16 fav2 = 0;
|
|
struct Game_NUM* game_num1 = CFG_get_game_num(hdr1->id);
|
|
struct Game_NUM* game_num2 = CFG_get_game_num(hdr2->id);
|
|
|
|
if (game_num1) fav1 = game_num1->favorite;
|
|
if (game_num2) fav2 = game_num2->favorite;
|
|
|
|
ret = (s32) (fav2-fav1);
|
|
if (ret == 0) return stricmp(get_title(hdr1), get_title(hdr2));
|
|
|
|
return ret;
|
|
}
|
|
/****************************************************************************
|
|
* Get PrevFilter
|
|
***************************************************************************/
|
|
|
|
int __Menu_GetPrevFilter(int t, wchar_t* gameFilter, u32 gameFiltered, wchar_t **PgameFilterPrev)
|
|
{
|
|
|
|
std::vector<wchar_t *> nameList;
|
|
|
|
struct discHdr *buffer = NULL;
|
|
u32 cnt, len, i;
|
|
s32 ret;
|
|
|
|
wchar_t *new_gameFilterPrev = wcsdup_new(gameFilter);
|
|
|
|
|
|
/* Get list length */
|
|
ret = WBFS_GetCount(&cnt);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* Buffer length */
|
|
len = sizeof(struct discHdr) * cnt;
|
|
|
|
/* Allocate memory */
|
|
buffer = (struct discHdr *)memalign(32, len);
|
|
if (!buffer)
|
|
return -1;
|
|
|
|
/* Clear buffer */
|
|
memset(buffer, 0, len);
|
|
|
|
/* Get header list */
|
|
ret = WBFS_GetHeaders(buffer, cnt, sizeof(struct discHdr));
|
|
if (ret < 0) {
|
|
if (buffer) free(buffer);
|
|
return ret;
|
|
}
|
|
/* Fill nameList */
|
|
for (i = 0; i < cnt; i++)
|
|
{
|
|
struct discHdr *header = &buffer[i];
|
|
|
|
/* Register game */
|
|
NewTitles::Instance()->CheckGame(header->id);
|
|
|
|
/* Filter Favorite */
|
|
if (Settings.fave && t==0)
|
|
{
|
|
struct Game_NUM* game_num = CFG_get_game_num(header->id);
|
|
if (!game_num || game_num->favorite==0)
|
|
continue;
|
|
}
|
|
/* Filter Pparental */
|
|
if (Settings.parentalcontrol && !Settings.godmode && t==0)
|
|
if(get_block(header) >= Settings.parentalcontrol)
|
|
continue;
|
|
|
|
/* Other parental control method */
|
|
if (Settings.parentalcontrol == 0 && Settings.godmode == 0 && Settings.parental.enabled == 1)
|
|
{
|
|
// Check game rating in WiiTDB, since the default Wii parental control setting is enabled
|
|
s32 rating = GetRatingForGame((char *) header->id);
|
|
|
|
if ((rating != -1 && rating > Settings.parental.rating) ||
|
|
(rating == -1 && get_pegi_block(header) > Settings.parental.rating))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
wchar_t *wname = FreeTypeGX::charToWideChar(get_title(header));
|
|
if(wname) nameList.push_back(wname);
|
|
}
|
|
|
|
NewTitles::Instance()->Save();
|
|
|
|
/* delete buffer */
|
|
if(buffer) free(buffer);
|
|
|
|
/* Find Prev-Filter */
|
|
len = wcslen(new_gameFilterPrev);
|
|
while(len)
|
|
{
|
|
cnt = 0;
|
|
new_gameFilterPrev[--len] = 0;
|
|
for(std::vector<wchar_t *>::iterator iter = nameList.begin(); iter < nameList.end(); iter++)
|
|
{
|
|
if(!wcsncmp(*iter, new_gameFilterPrev, len))
|
|
cnt++;
|
|
}
|
|
if(cnt > gameFiltered)
|
|
break;
|
|
}
|
|
/* Clean name List */
|
|
for(std::vector<wchar_t *>::iterator iter = nameList.begin(); iter < nameList.end(); iter++)
|
|
delete [] *iter;
|
|
|
|
if(PgameFilterPrev)
|
|
*PgameFilterPrev = new_gameFilterPrev;
|
|
|
|
return 0;
|
|
}
|
|
/****************************************************************************
|
|
* Get GameFilter NextList
|
|
***************************************************************************/
|
|
|
|
int int_cmp(const void *a, const void *b) { return *((u32*)a)-*((u32*)b); }
|
|
|
|
int __Menu_GetGameFilter_NextList(discHdr *gameList, u32 gameCnt, wchar_t **PgameFilter, wchar_t **PgameFilterNextList)
|
|
{
|
|
u32 filter_len = wcslen(*PgameFilter);
|
|
u32 i, lastChar=0;
|
|
bool autofill = filter_len > 0; // autofill only when gameFilter is not empty
|
|
wchar_t *p;
|
|
u32 *nextList = new u32[gameCnt]; if(nextList==NULL) return -1;
|
|
for(i=0; i<gameCnt; i++)
|
|
{
|
|
u32 nextFilterChar = 0x10000;
|
|
wchar_t *gameName = FreeTypeGX::charToWideChar(get_title(&gameList[i]));
|
|
if(gameName == NULL) goto error;
|
|
|
|
if(wcslen(gameName) > filter_len)
|
|
{
|
|
if((filter_len == 0 || !wcsnicmp(*PgameFilter, gameName, filter_len)))
|
|
nextFilterChar = towupper(gameName[filter_len]);
|
|
}
|
|
else if(wcslen(gameName) == filter_len)
|
|
autofill = false; // no autofill when gameNameLen == filterLen
|
|
|
|
nextList[i] = nextFilterChar;
|
|
}
|
|
qsort(nextList, gameCnt, sizeof(u32), int_cmp);
|
|
|
|
*PgameFilterNextList = new wchar_t[gameCnt+1];
|
|
if(*PgameFilterNextList == NULL) goto error;
|
|
|
|
|
|
p = *PgameFilterNextList;
|
|
lastChar = 0;
|
|
for(i=0; i<gameCnt; i++)
|
|
{
|
|
u32 Char = nextList[i];
|
|
if(lastChar != Char)
|
|
{
|
|
if(Char == 0x10000)
|
|
break;
|
|
*p++ = lastChar = Char;
|
|
}
|
|
}
|
|
*p = 0;
|
|
if(wcslen(*PgameFilterNextList) == 1 && autofill) // only one nextChar and autofill is true
|
|
{
|
|
wchar_t *newFilter = new wchar_t[filter_len + 2];
|
|
if(newFilter == NULL) goto error;
|
|
|
|
wcscpy(newFilter, *PgameFilter);
|
|
wcscat(newFilter, *PgameFilterNextList);
|
|
delete [] *PgameFilter; *PgameFilter = newFilter;
|
|
delete [] *PgameFilterNextList; *PgameFilterNextList = NULL;
|
|
return __Menu_GetGameFilter_NextList(gameList, gameCnt, PgameFilter, PgameFilterNextList);
|
|
}
|
|
|
|
return 0;
|
|
error:
|
|
if(nextList) delete [] nextList;
|
|
if(*PgameFilterNextList) delete [] *PgameFilterNextList;
|
|
return -1;
|
|
}
|
|
|
|
int buildTitleList(int t, wchar_t* gameFilter, discHdr ** PgameList, u32 *PgameCnt){
|
|
|
|
struct discHdr *buffer = NULL;
|
|
u32 i = 0;
|
|
u32 cnt, cnt2=0, len;
|
|
u32 num_titles;
|
|
u32 titles[100] ATTRIBUTE_ALIGN(32);
|
|
u32 num_sys_titles;
|
|
u32 sys_titles[10] ATTRIBUTE_ALIGN(32);
|
|
s32 ret = -1;
|
|
FILE *f;
|
|
char path[100];
|
|
|
|
ISFS_Initialize();
|
|
|
|
ret = getTitles_TypeCount(typei, &num_titles);
|
|
if (ret < 0) {
|
|
return -1;
|
|
}
|
|
|
|
ret = getTitles_Type(typei, titles, num_titles);
|
|
if (ret < 0) {
|
|
return -2;
|
|
}
|
|
|
|
ret = getTitles_TypeCount(0x00010002, &num_sys_titles);
|
|
if (ret < 0) {
|
|
return -3;
|
|
}
|
|
|
|
ret = getTitles_Type(0x00010002, sys_titles, num_sys_titles);
|
|
if (ret < 0) {
|
|
return -4;
|
|
}
|
|
|
|
cnt = (num_titles+num_sys_titles);
|
|
len = sizeof(struct discHdr) * cnt;
|
|
buffer = (struct discHdr *)memalign(32, len);
|
|
if (!buffer)
|
|
return -1;
|
|
|
|
memset(buffer, 0, len);
|
|
|
|
sprintf(path,"%s/config/database.txt",bootDevice);
|
|
f = fopen(path, "r");
|
|
|
|
char name[64];
|
|
while (i < (num_titles+num_sys_titles)) {
|
|
//start from the beginning of the file each loop
|
|
if (f)rewind(f);
|
|
char text[15];
|
|
strcpy(name,"");//make sure name is empty
|
|
u8 found=0;
|
|
|
|
sprintf(text, "%s", titleText(i<num_titles?typei:0x00010002, i<num_titles?titles[i]:sys_titles[i-num_titles]));
|
|
|
|
|
|
char line[200];
|
|
char tmp[50];
|
|
snprintf(tmp,50," ");
|
|
|
|
//check if the content.bin is on the SD card for that game
|
|
//if there is content.bin,then the game is on the SDmenu and not the wii
|
|
sprintf(line,"SD:/private/wii/title/%s/content.bin",text);
|
|
if (!checkfile(line))
|
|
{
|
|
|
|
struct discHdr *header = &buffer[i];
|
|
if (f) {
|
|
while (fgets(line, sizeof(line), f)) {
|
|
if (line[0]== text[0]&&
|
|
line[1]== text[1]&&
|
|
line[2]== text[2]) {
|
|
int j=0;
|
|
found=1;
|
|
for (j=0;(line[j+4]!='\0' || j<51);j++)
|
|
|
|
tmp[j]=line[j+4];
|
|
snprintf(header->title,sizeof(header->title),"%s",tmp);
|
|
//break;
|
|
}
|
|
}
|
|
}
|
|
if (!found) {
|
|
if (getName00(header->title, TITLE_ID(i<num_titles?typei:0x00010002, i<num_titles?titles[i]:sys_titles[i-num_titles]),CONF_GetLanguage()*2)>=0)
|
|
found=2;
|
|
|
|
if (!found) {
|
|
if (getNameBN(header->title, TITLE_ID(i<num_titles?typei:0x00010002, i<num_titles?titles[i]:sys_titles[i-num_titles]))>=0)
|
|
found=3;
|
|
|
|
if (!found)
|
|
snprintf(header->title,sizeof(header->title),"%s (%08x)",text,i<num_titles?titles[i]:sys_titles[i-num_titles]);
|
|
}
|
|
}
|
|
//put the 4th and 8th digit of the title type as the last 2 characters here
|
|
//no reason other than it will let us know how we should boot the title later
|
|
header->id[0]=text[0];
|
|
header->id[1]=text[1];
|
|
header->id[2]=text[2];
|
|
header->id[3]=text[3];
|
|
header->id[4]='1';
|
|
header->id[5]=(i<num_titles?'1':'2');
|
|
//header->
|
|
|
|
//not using these filters right now, but i left them in just in case
|
|
// Filters
|
|
/*if (Settings.fave) {
|
|
struct Game_NUM* game_num = CFG_get_game_num(header->id);
|
|
if (!game_num || game_num->favorite==0)
|
|
continue;
|
|
}
|
|
|
|
if (Settings.parentalcontrol && !Settings.godmode) {
|
|
if (get_block(header) >= Settings.parentalcontrol)
|
|
continue;
|
|
}*/
|
|
|
|
if(gameFilter && *gameFilter) {
|
|
u32 filter_len = wcslen(gameFilter);
|
|
wchar_t *gameName = FreeTypeGX::charToWideChar(get_title(header));
|
|
if (!gameName || wcsnicmp(gameName, gameFilter, filter_len)) {
|
|
delete [] gameName;
|
|
continue;
|
|
}
|
|
}
|
|
if(i != cnt2)
|
|
buffer[cnt2] = buffer[i];
|
|
cnt2++;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if (f)fclose(f);
|
|
|
|
Uninstall_FromTitle(TITLE_ID(1, 0));
|
|
ISFS_Deinitialize();
|
|
|
|
if(cnt > cnt2)
|
|
{
|
|
cnt = cnt2;
|
|
buffer = (struct discHdr *)realloc(buffer, sizeof(struct discHdr) * cnt);
|
|
}
|
|
if (!buffer)
|
|
return -1;
|
|
|
|
if (Settings.sort==pcount) {
|
|
qsort(buffer, cnt, sizeof(struct discHdr), __Menu_EntryCmpCount);
|
|
} else if (Settings.fave) {
|
|
qsort(buffer, cnt, sizeof(struct discHdr), __Menu_EntryCmpFavorite);
|
|
} else {
|
|
qsort(buffer, cnt, sizeof(struct discHdr), __Menu_EntryCmp);
|
|
}
|
|
/*PgameList = buffer;
|
|
buffer = NULL;
|
|
PgameCnt = cnt;*/
|
|
|
|
if(PgameList) *PgameList = buffer; else free(buffer);
|
|
if(PgameCnt) *PgameCnt = cnt;
|
|
|
|
return 0;
|
|
|
|
return cnt;
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
* Get Gamelist
|
|
***************************************************************************/
|
|
int __Menu_GetGameList(int t, wchar_t* gameFilter, discHdr ** PgameList, u32 *PgameCnt) {
|
|
struct discHdr *buffer = NULL;
|
|
|
|
u32 cnt, cnt2=0, len;
|
|
s32 ret;
|
|
|
|
/* Get list length */
|
|
ret = WBFS_GetCount(&cnt);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* Buffer length */
|
|
len = sizeof(struct discHdr) * cnt;
|
|
|
|
/* Allocate memory */
|
|
buffer = (struct discHdr *)memalign(32, len);
|
|
if (!buffer)
|
|
return -1;
|
|
|
|
/* Clear buffer */
|
|
memset(buffer, 0, len);
|
|
|
|
/* Get header list */
|
|
ret = WBFS_GetHeaders(buffer, cnt, sizeof(struct discHdr));
|
|
if (ret < 0) {
|
|
if (buffer) free(buffer);
|
|
return ret;
|
|
}
|
|
|
|
for (u32 i = 0; i < cnt; i++) {
|
|
struct discHdr *header = &buffer[i];
|
|
|
|
/* Register game */
|
|
NewTitles::Instance()->CheckGame(header->id);
|
|
|
|
/* Filters */
|
|
if (Settings.fave && t==0) {
|
|
struct Game_NUM* game_num = CFG_get_game_num(header->id);
|
|
if (!game_num || game_num->favorite==0)
|
|
continue;
|
|
}
|
|
|
|
//ignore uLoader cfg "iso". i was told it is "__CFG_" but not confirmed
|
|
if (header->id[0]=='_'&&header->id[1]=='_'&&
|
|
header->id[2]=='C'&&header->id[3]=='F'&&
|
|
header->id[4]=='G'&&header->id[5]=='_')
|
|
continue;
|
|
|
|
if (Settings.parentalcontrol && !Settings.godmode && t==0) {
|
|
if (get_block(header) >= Settings.parentalcontrol)
|
|
continue;
|
|
}
|
|
|
|
/* Other parental control method */
|
|
if (Settings.parentalcontrol == 0 && Settings.godmode == 0 && Settings.parental.enabled == 1 && t==0)
|
|
{
|
|
// Check game rating in WiiTDB, since the default Wii parental control setting is enabled
|
|
s32 rating = GetRatingForGame((char *) header->id);
|
|
if ((rating != -1 && rating > Settings.parental.rating) ||
|
|
(rating == -1 && get_pegi_block(header) > Settings.parental.rating))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if(gameFilter && *gameFilter && t==0) {
|
|
u32 filter_len = wcslen(gameFilter);
|
|
wchar_t *gameName = FreeTypeGX::charToWideChar(get_title(header));
|
|
if (!gameName || wcsnicmp(gameName, gameFilter, filter_len)) {
|
|
delete [] gameName;
|
|
continue;
|
|
}
|
|
}
|
|
if(i != cnt2)
|
|
buffer[cnt2] = buffer[i];
|
|
cnt2++;
|
|
}
|
|
NewTitles::Instance()->Save();
|
|
|
|
if(cnt > cnt2)
|
|
{
|
|
cnt = cnt2;
|
|
buffer = (struct discHdr *)realloc(buffer, sizeof(struct discHdr) * cnt);
|
|
}
|
|
if (!buffer)
|
|
return -1;
|
|
|
|
if (Settings.sort==pcount) {
|
|
qsort(buffer, cnt, sizeof(struct discHdr), __Menu_EntryCmpCount);
|
|
} else if (Settings.fave) {
|
|
qsort(buffer, cnt, sizeof(struct discHdr), __Menu_EntryCmpFavorite);
|
|
} else {
|
|
qsort(buffer, cnt, sizeof(struct discHdr), __Menu_EntryCmp);
|
|
}
|
|
|
|
/* Set values */
|
|
if(PgameList) *PgameList = buffer; else free(buffer);
|
|
if(PgameCnt) *PgameCnt = cnt;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int __Menu_GetEntries(int t, const wchar_t* Filter) {
|
|
|
|
/*if (mountMethod==3)
|
|
{
|
|
return buildTitleList();
|
|
}*/
|
|
|
|
|
|
u32 new_gameCnt = 0;
|
|
struct discHdr *new_gameList = NULL;
|
|
wchar_t *new_gameFilter = NULL;
|
|
wchar_t *new_gameFilterNextList = NULL;
|
|
wchar_t *new_gameFilterPrev = NULL;
|
|
|
|
new_gameFilter = wcsdup_new(Filter ? Filter : (gameFilter ? gameFilter : L"") );
|
|
if(new_gameFilter == NULL) return -1;
|
|
|
|
for(;;)
|
|
{
|
|
if (mountMethod==3)
|
|
{if(buildTitleList(t, new_gameFilter, &new_gameList, &new_gameCnt) < 0)
|
|
return -1;}
|
|
|
|
else
|
|
{if(__Menu_GetGameList(t, new_gameFilter, &new_gameList, &new_gameCnt) < 0)
|
|
return -1;}
|
|
|
|
|
|
if(new_gameCnt > 0 || new_gameFilter[0] == 0)
|
|
break;
|
|
new_gameFilter[wcslen(new_gameFilter)-1] = 0;
|
|
}
|
|
|
|
/* init GameFilterNextList */
|
|
if(__Menu_GetGameFilter_NextList(new_gameList, new_gameCnt, &new_gameFilter, &new_gameFilterNextList) < 0)
|
|
goto error;
|
|
|
|
/* init GameFilterPrev */
|
|
if(__Menu_GetPrevFilter(t, new_gameFilter, new_gameCnt, &new_gameFilterPrev) < 0)
|
|
goto error;
|
|
|
|
/* Set values */
|
|
if(gameList) free(gameList);
|
|
if(gameFilter) delete [] gameFilter;
|
|
if(gameFilterNextList) delete [] gameFilterNextList;
|
|
if(gameFilterPrev) delete [] gameFilterPrev;
|
|
|
|
gameList = new_gameList;
|
|
gameCnt = new_gameCnt;
|
|
gameFilter = new_gameFilter;
|
|
gameFilterNextList = new_gameFilterNextList;
|
|
gameFilterPrev = new_gameFilterPrev;
|
|
|
|
/* Reset variables */
|
|
gameSelected = gameStart = 0;
|
|
return 0;
|
|
error: // clean up
|
|
if(new_gameList) free(new_gameList);
|
|
if(new_gameFilter) delete [] new_gameFilter;
|
|
if(new_gameFilterNextList) delete [] new_gameFilterNextList;
|
|
if(new_gameFilterPrev) delete [] new_gameFilterPrev;
|
|
return -1;
|
|
}
|