mirror of
https://github.com/wiidev/usbloadergx.git
synced 2024-11-04 18:45:05 +01:00
a09abe355f
* Fixed issue 1058 * Menus splitted to several smaller files, to reduce compile time This version has FAT support. You can change the used partition in the game load options. Default WBFS will be used, if found. Otherwise the first FAT partition with games will be used. FAT will only work when using Hermes cios (222/223)!!!
584 lines
14 KiB
C++
584 lines
14 KiB
C++
#include <string.h>
|
|
|
|
#include "settings/cfg.h"
|
|
#include "usbloader/wbfs.h"
|
|
#include "main.h"
|
|
#include <wctype.h>
|
|
#include "getentries.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];
|
|
|
|
/* 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;
|
|
|
|
wchar_t *wname = FreeTypeGX::charToWideChar(get_title(header));
|
|
if(wname) nameList.push_back(wname);
|
|
}
|
|
|
|
/* 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];
|
|
|
|
/* 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;
|
|
}
|
|
|
|
if (Settings.parentalcontrol && !Settings.godmode && t==0) {
|
|
if (get_block(header) >= Settings.parentalcontrol)
|
|
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++;
|
|
}
|
|
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;
|
|
}
|