CfgUSBLoader/source/menu.c
Nitraiolo c9df95e9d1 GC - updated nintendont config support to version 0x00000006
GC - if nintendont_config_mode=arg is set nintendont configuration is passed via command line argument
GC - added minimum nitnendont version check before game load
GC - added possibility to update nintendont as a plugin if update_nintendont=1 is set
Internals - added UStealth support
Internals - fixed plugins update URLs
Usability - fixed reboot and exit behaviour
Usability - WFC patching options
2017-01-15 09:56:47 +00:00

5814 lines
139 KiB
C
Raw Blame History

// Modified by oggzee & usptactical
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <ogcsys.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <stdarg.h>
#include <ctype.h>
#include <wctype.h>
#include <wchar.h>
#include <locale.h>
#include <asndlib.h>
#include <dirent.h>
#include <gccore.h>
#include <ogc/ipc.h>
#include <ogc/lwp_threads.h>
#include <ogc/machine/processor.h>
#include <fat.h>
#include "disc.h"
#include "fat.h"
#include "cache.h"
#include "gui.h"
#include "menu.h"
#include "partition.h"
#include "restart.h"
#include "sys.h"
#include "util.h"
#include "utils.h"
#include "video.h"
#include "wbfs.h"
#include "libwbfs/libwbfs.h"
#include "wpad.h"
#include "patchcode.h"
#include "cfg.h"
#include "http.h"
#include "dns.h"
#include "wdvd.h"
#include "music.h"
#include "subsystem.h"
#include "net.h"
#include "fst.h"
#include "wiip.h"
#include "frag.h"
#include "xml.h" /* WiiTDB database - Lustar */
#include "sort.h"
#include "gettext.h"
#include "playlog.h"
#include "dolmenu.h"
#include "gc.h"
#include "gc_wav.h"
#include "fileOps.h"
#include "sdhc.h"
#include "nand.h"
#include "dol.h"
#include "savegame.h"
#include "channel.h"
#include "RuntimeIOSPatch.h"
#include "titles.h"
void _unstub_start();
void* dolchunkoffset[64];
u32 dolchunksize[64];
u32 dolchunkcount;
extern void __exception_closeall();
typedef void (*entrypoint) (void);
#define CHANGE(V,M) {V+=change; if(V>(M)) V=(M); if(V<0) V=0;}
#define HW_PPCSPEED ((vu32*)0xCD800018)
char CFG_VERSION[] = CFG_VERSION_STR;
void Sys_Exit();
/* Gamelist buffer */
struct discHdr *all_gameList = NULL;
struct discHdr *fav_gameList = NULL;
struct discHdr *gameList = NULL;
struct discHdr *filter_gameList = NULL;
/* Gamelist variables */
bool enable_favorite = false;
s32 all_gameCnt = 0;
s32 fav_gameCnt = 0;
s32 filter_gameCnt = 0;
extern s32 filter_type;
extern s32 filter_index;
bool MountWBFS = true;
s32 gameCnt = 0, gameSelected = 0, gameStart = 0, chanSelected = 0, chanStart = 0, mycount = 0;
bool imageNotFound = false;
/* admin unlock mode variables */
static int disable_format = 0;
static int disable_remove = 0;
static int disable_install = 0;
static int disable_options = 0;
static int confirm_start = 1;
static bool unlock_init = true;
extern char wbfs_fs_drive[16];
extern char dm_boot_drive[16];
char *videos[CFG_VIDEO_NUM] =
{
gts("System Def."),
gts("Game Default"),
gts("Force PAL50"),
gts("Force PAL60"),
gts("Force NTSC")
};
char *DML_videos[6] =
{
gts("Auto"),
gts("Force PAL"),
gts("Force NTSC"),
gts("Force PAL60"),
gts("Force NTSC 480p"),
gts("Force PAL 480p")
};
char *languages[CFG_LANG_NUM] =
{
gts("Auto"),
gts("Japanese"),
gts("English"),
gts("German"),
gts("French"),
gts("Spanish"),
gts("Italian"),
gts("Dutch"),
gts("S. Chinese"),
gts("T. Chinese"),
gts("Korean")
};
char *playlog_name[4] =
{
gts("Off"),
gts("On"),
gts("Japanese Title"),
gts("English Title")
};
char *str_wiird[3] =
{
gts("Off"),
gts("On"),
gts("Paused Start")
};
char *str_dml[8] =
{
gts("Auto"),
gts("DEVO"),
gts("r51-"),
gts("r52+"),
gts("1.2+"),
gts("2.0+"),
gts("2.1+"),
gts("2.2+")
};
char *str_nand_emu[3] =
{
gts("Off"),
gts("Partial"),
gts("Full")
};
char *str_channel_boot[2] =
{
gts("Mighty Plugin"),
gts("Neek2o Plugin")
};
char *str_gc_boot[4] =
{
gts("Default"),
gts("DIOS MIOS"),
gts("Devolution"),
gts("Nintendont")
};
char *str_mem_card_size[6] =
{
gts(" 59 blocks"),
gts(" 123 blocks"),
gts(" 251 blocks"),
gts(" 507 blocks"),
gts("1019 blocks"),
gts("2043 blocks")
};
char *str_private_server[4] =
{
gts("Off"),
gts("NoSSL only"),
gts("wiimmfi.de"),
gts("Custom")
};
int Menu_Global_Options();
int Menu_Game_Options();
void Switch_Favorites(bool enable);
void bench_io();
char action_string[40] = "";
#ifdef FAKE_GAME_LIST
// Debug Test mode with fake game list
#define WBFS_GetCount dbg_WBFS_GetCount
#define WBFS_GetHeaders dbg_WBFS_GetHeaders
#endif
char *__Menu_PrintTitle(char *name)
{
//static char buffer[MAX_CHARACTERS + 4];
static char buffer[400];
int len = con_len(name);
/* Clear buffer */
memset(buffer, 0, sizeof(buffer));
/* Check string length */
if (len >= MAX_CHARACTERS) {
//strncpy(buffer, name, MAX_CHARACTERS - 4);
STRCOPY(buffer, name);
con_trunc(buffer, MAX_CHARACTERS - 4);
strcat(buffer, "..");
return buffer;
}
return name;
}
void __Chan_MoveList(s8 delta)
{
/* No channellist */
if (!mycount)
return;
#if 0
/* Select next entry */
chanSelected += delta;
/* Out of the list? */
if (chanSelected <= -1)
chanSelected = (mycount - 1);
if (chanSelected >= mycount)
chanSelected = 0;
#endif
if(delta>0) {
if(chanSelected == mycount - 1) {
chanSelected = 0;
}
else {
chanSelected +=delta;
if(chanSelected >= mycount) {
chanSelected = (mycount - 1);
}
}
}
else {
if(!chanSelected) {
chanSelected = mycount - 1;
}
else {
chanSelected +=delta;
if(chanSelected < 0) {
chanSelected = 0;
}
}
}
/* List scrolling */
s32 index = (chanSelected - chanStart);
if (index >= ENTRIES_PER_PAGE)
chanStart += index - (ENTRIES_PER_PAGE - 1);
if (index <= -1)
chanStart += index;
}
char *skip_sort_ignore(char *s)
{
char tok[200], *p;
int len;
p = CFG.sort_ignore;
while (p) {
p = split_token(tok, p, ',', sizeof(tok));
len = strlen(tok);
if (len && strncasecmp(s, tok, strlen(tok)) == 0) {
if (s[len] == ' ' || s[len] == '\'') {
s += len + 1;
}
}
}
return s;
}
struct discHdr *getHeaderById(struct discHdr *list, int cnt, u8 *id, u8 disc) {
u32 i = 0;
for (i=0; i < cnt; i++) {
struct discHdr *header = list+i;
if (!memcmp(header->id, id, 6) && header->disc == disc) return header;
}
return NULL;
}
void Setup_NandEmu(u8 NandEmuMode, const char *NandEmuPath)
{
if(NandEmuMode && strchr(NandEmuPath, '/'))
{
if (!strncmp(NandEmuPath, "usb", 3) || !strncmp(NandEmuPath, "sd", 2))
{
int partition = 0;
dbg_printf("Enabling Nand Emulation on: %s\n", NandEmuPath);
Set_FullMode(NandEmuMode == 2);
Set_Path(strchr(NandEmuPath, '/'));
//! Unmount devices to flush data before activating NAND Emu
if(strncmp(NandEmuPath, "usb", 3) == 0)
{
//! Set which partition to use (USB only)
partition = atoi(NandEmuPath+3)-1;
Set_Partition(partition);
}
Enable_Emu(get_nand_device());
//! Mount USB to start game, SD is not required
if(strncmp(NandEmuPath, "usb", 3) == 0) {
CFG_MountUSB();
mount_find(USB_DRIVE);
}
}
}
}
bool unwanted_channel(const char *d_name)
{
if (!strncmp(d_name, ".", 1) // current directory
|| !strncmp(d_name, "..", 2) // previous directory
|| !strncmp(d_name, "48414141", 8) // H A A A Photo channel 1.0
|| !strncmp(d_name, "af1bf516", 8) // B o o t M i i
|| !strncmp(d_name, "4a4f4449", 8) // J O D I home brew channel
|| !strncmp(d_name, "48415858", 8) // H A X X home brew channel
|| !strncmp(d_name, "55435846", 8) // U C X F cfg loader forwarder
|| !strncmp(d_name, "4d415549", 8) // M A U I backup homebrew channel
|| !strncmp(d_name, "4e454f47", 8) // N E O G backup disk channel
)
return true;
else
return false;
}
s32 get_channel_list(void *outbuf, u32 size)
{
s32 ret = 0;
static char path_buffer[255] ATTRIBUTE_ALIGN(32);
struct dirent *entry;
FILE *fp;
DIR *sdir;
u8 *buffer = allocate_memory(0x1E4+4);
char gamePath[255];
sprintf(gamePath, "%s/title/00010001", CFG.nand_emu_path);
sdir = opendir(gamePath);
if (sdir) do
{
entry = readdir(sdir);
if (entry)
{
// sprintf(path, "%s/title/00010001/%s", CFG.nand_emu_path, entry->d_name);
if (unwanted_channel(entry->d_name))
{
continue;
}
sprintf(path_buffer, "%s/title/00010001/%s/content/title.tmd", CFG.nand_emu_path, entry->d_name);
fp = fopen(path_buffer, "rb");
if (fp)
{
fread(buffer, 1, 0x1E4+4, fp);
fclose(fp);
u32 bannerContent = 0;
memcpy(&bannerContent, buffer+0x1E4, 4);
sprintf(path_buffer, "%s/title/00010001/%s/content/%08x.app", CFG.nand_emu_path, entry->d_name, bannerContent);
fp = fopen(path_buffer, "rb");
if (fp)
{
u8 *ptr = ((u8 *)outbuf) + (ret * sizeof(struct discHdr));
struct discHdr *channel = (struct discHdr *)ptr;
channel->magic = CHANNEL_MAGIC;
//strncpy(channel->path, path, strlen(path));
u32 title_low = TITLE_LOWER(strtol(entry->d_name,NULL,16));
memcpy(channel->id, &title_low, sizeof(u32));
char *name = get_channel_name(TITLE_ID(0x00010001, title_low),fp);
strncpy(channel->title, name, sizeof(channel->title));
SAFE_FREE(name);
ret++;
fclose(fp);
}
}
}
} while (entry);
if (sdir) closedir(sdir);
SAFE_FREE(buffer);
return ret;
}
s32 get_channel_list_cnt()
{
s32 ret = 0;
struct dirent *entry;
DIR *sdir;
char gamePath[255];
sprintf(gamePath, "%s/title/00010001", CFG.nand_emu_path);
dbg_printf("\nSearching channel games in %s ...\n", gamePath);
sdir = opendir(gamePath);
if (sdir) do
{
entry = readdir(sdir);
if (entry)
{
ret++; //assume if the dir is their that it is a valid channel
//No need to actually open the files here will be done during the actual load
//This is much faster but uses slightly more mem while loading
}
} while (entry);
if (sdir) closedir(sdir);
return ret;
}
s32 get_DML_game_list(void *outbuf)
{
FILE *fp;
u32 DML_GameCount = 0;
static char name_buffer[64] ATTRIBUTE_ALIGN(32);
DIR *sdir;
DIR *s2dir;
struct dirent *entry;
// 1st count the number of games on SD
char gamePath[255];
sprintf(gamePath, "%s/games", dm_boot_drive);
dbg_printf("\nSearching GC games in %s ...\n", gamePath);
sdir = opendir(gamePath);
if (sdir) do
{
entry = readdir(sdir);
if (entry)
{
sprintf(name_buffer, "%s/games/%s", dm_boot_drive, entry->d_name);
if (!strncmp(entry->d_name, ".", 1) || !strncmp(entry->d_name, "..", 2))
{
continue;
}
s2dir = opendir(name_buffer);
if (s2dir)
{
sprintf(name_buffer, "%s/games/%s/game.iso", dm_boot_drive, entry->d_name);
fp = fopen(name_buffer, "rb");
if (fp)
{
fseek(fp, 0, SEEK_END);
if (ftell(fp) > 1000000)
{
u8 *ptr = ((u8 *)outbuf) + (DML_GameCount * sizeof(struct discHdr));
struct discHdr *dmlGame = (struct discHdr *)ptr;
struct gc_discHdr header;
fseek(fp, 0, SEEK_SET);
fread(&header, 1, sizeof(struct gc_discHdr), fp);
fclose(fp);
if (header.magic == GC_MAGIC && !getHeaderById(outbuf, DML_GameCount, header.id, header.disc)) {
memcpy(dmlGame->id, header.id, 6);
dmlGame->disc = header.disc;
memcpy(dmlGame->title, header.title, sizeof(dmlGame->title));
sprintf(dmlGame->path, "%s/games/%s", dm_boot_drive, entry->d_name);
dmlGame->magic = GC_GAME_ON_DRIVE;
DML_GameCount++;
}
}
fclose(fp);
}
else
{
sprintf(name_buffer, "%s/games/%s/sys/boot.bin", dm_boot_drive, entry->d_name);
fp = fopen(name_buffer, "rb");
if (fp)
{
u8 *ptr = ((u8 *)outbuf) + (DML_GameCount * sizeof(struct discHdr));
struct discHdr *dmlGame = (struct discHdr *)ptr;
struct gc_discHdr header;
fseek(fp, 0, SEEK_SET);
fread(&header, 1, sizeof(struct gc_discHdr), fp);
fclose(fp);
if (header.magic == GC_MAGIC && !getHeaderById(outbuf, DML_GameCount, header.id, header.disc)) {
memcpy(dmlGame->id, header.id, 6);
dmlGame->disc = header.disc;
memcpy(dmlGame->title, header.title, sizeof(dmlGame->title));
sprintf(dmlGame->path, "%s/games/%s", dm_boot_drive, entry->d_name);
dmlGame->magic = GC_GAME_ON_DRIVE;
DML_GameCount++;
}
}
}
closedir(s2dir);
}
}
} while (entry);
if (sdir) closedir(sdir);
sdir = NULL;
if (strncmp(wbfs_fs_drive, dm_boot_drive, strlen(dm_boot_drive))) {
// 2st count the number of games on hdd+
char filepath[0xFF];
sprintf(filepath, "%s/games", wbfs_fs_drive);
dbg_printf("\nSearching GC games in %s ...\n", filepath);
sdir = opendir(filepath);
if (sdir) do
{
entry = readdir(sdir);
if (entry)
{
sprintf(name_buffer, "%s/games/%s", wbfs_fs_drive, entry->d_name);
if (!strncmp(entry->d_name, ".", 1) || !strncmp(entry->d_name, "..", 2))
{
continue;
}
s2dir = opendir(name_buffer);
if(s2dir)
{
sprintf(name_buffer, "%s/games/%s/game.iso", wbfs_fs_drive, entry->d_name);
fp = fopen(name_buffer, "rb");
if(fp)
{
u8 *ptr = ((u8 *)outbuf) + (DML_GameCount * sizeof(struct discHdr));
struct discHdr *dmlGame = (struct discHdr *)ptr;
struct gc_discHdr header;
fseek(fp, 0, SEEK_SET);
fread(&header, 1, sizeof(struct gc_discHdr), fp);
fclose(fp);
if (header.magic == GC_MAGIC && !getHeaderById(outbuf, DML_GameCount, header.id, header.disc)) {
memcpy(dmlGame->id, header.id, 6);
dmlGame->disc = header.disc;
memcpy(dmlGame->title, header.title, sizeof(dmlGame->title));
sprintf(dmlGame->path, "%s/games/%s", wbfs_fs_drive, entry->d_name);
dmlGame->magic = GC_GAME_ON_DRIVE;
DML_GameCount++;
}
}
else
{
sprintf(name_buffer, "%s/games/%s/sys/boot.bin", wbfs_fs_drive, entry->d_name);
fp = fopen(name_buffer, "rb");
if(fp)
{
u8 *ptr = ((u8 *)outbuf) + (DML_GameCount * sizeof(struct discHdr));
struct discHdr *dmlGame = (struct discHdr *)ptr;
struct gc_discHdr header;
fseek(fp, 0, SEEK_SET);
fread(&header, 1, sizeof(struct gc_discHdr), fp);
fclose(fp);
if (header.magic == GC_MAGIC && !getHeaderById(outbuf, DML_GameCount, header.id, header.disc)) {
memcpy(dmlGame->id, header.id, 6);
dmlGame->disc = header.disc;
memcpy(dmlGame->title, header.title, sizeof(dmlGame->title));
sprintf(dmlGame->path, "%s/games/%s", wbfs_fs_drive, entry->d_name);
dmlGame->magic = GC_GAME_ON_DRIVE;
DML_GameCount++;
}
}
}
closedir(s2dir);
}
}
} while (entry);
if (sdir) closedir(sdir);
sdir = NULL;
}
if (strncmp(dm_boot_drive, "sd:", 3) && strncmp(wbfs_fs_drive, "sd:", 3)) {
// 3th count the number of games on hdd
char filepath[0xFF];
sprintf(filepath, "sd:/games");
dbg_printf("\nSearching GC games in %s ...\n", filepath);
sdir = opendir(filepath);
if (sdir) do
{
entry = readdir(sdir);
if (entry)
{
sprintf(name_buffer, "sd:/games/%s", entry->d_name);
if (!strncmp(entry->d_name, ".", 1) || !strncmp(entry->d_name, "..", 2))
{
continue;
}
s2dir = opendir(name_buffer);
if(s2dir)
{
sprintf(name_buffer, "sd:/games/%s/game.iso", entry->d_name);
fp = fopen(name_buffer, "rb");
if(fp)
{
u8 *ptr = ((u8 *)outbuf) + (DML_GameCount * sizeof(struct discHdr));
struct discHdr *dmlGame = (struct discHdr *)ptr;
struct gc_discHdr header;
fseek(fp, 0, SEEK_SET);
fread(&header, 1, sizeof(struct gc_discHdr), fp);
fclose(fp);
if (header.magic == GC_MAGIC && !getHeaderById(outbuf, DML_GameCount, header.id, header.disc)) {
memcpy(dmlGame->id, header.id, 6);
dmlGame->disc = header.disc;
memcpy(dmlGame->title, header.title, sizeof(dmlGame->title));
sprintf(dmlGame->path, "sd:/games/%s", entry->d_name);
dmlGame->magic = GC_GAME_ON_DRIVE;
DML_GameCount++;
}
}
else
{
sprintf(name_buffer, "sd:/games/%s/sys/boot.bin", entry->d_name);
fp = fopen(name_buffer, "rb");
if(fp)
{
u8 *ptr = ((u8 *)outbuf) + (DML_GameCount * sizeof(struct discHdr));
struct discHdr *dmlGame = (struct discHdr *)ptr;
struct gc_discHdr header;
fseek(fp, 0, SEEK_SET);
fread(&header, 1, sizeof(struct gc_discHdr), fp);
fclose(fp);
if (header.magic == GC_MAGIC && !getHeaderById(outbuf, DML_GameCount, header.id, header.disc)) {
memcpy(dmlGame->id, header.id, 6);
dmlGame->disc = header.disc;
memcpy(dmlGame->title, header.title, sizeof(dmlGame->title));
sprintf(dmlGame->path, "sd:/games/%s", entry->d_name);
dmlGame->magic = GC_GAME_ON_DRIVE;
DML_GameCount++;
}
}
}
closedir(s2dir);
}
}
} while (entry);
dbg_printf("Closing dir\n");
if (sdir) closedir(sdir);
dbg_printf("Dir closed\n");
sdir = NULL;
}
return DML_GameCount;
}
s32 __Menu_GetEntries(void)
{
struct discHdr *buffer = NULL;
struct discHdr *dmlBuffer = NULL;
struct discHdr *channelBuffer = NULL;
u32 cnt = 0, len = 0;
s32 ret = 0;
s32 dml = 0;
s32 channelCnt = 0;
Cache_Invalidate();
Switch_Favorites(false);
SAFE_FREE(fav_gameList);
fav_gameCnt = 0;
SAFE_FREE(filter_gameList);
filter_gameCnt = 0;
/* Free memory */
if (all_gameList) {
free(all_gameList);
all_gameList = NULL;
all_gameCnt = 0;
}
/* we just cleared all possable places gameList could be poinying so it must be clear */
gameList = NULL;
gameCnt = 0;
if (MountWBFS) {
/* Get list length */
ret = WBFS_GetCount(&cnt);
if (ret < 0)
return ret;
}
/* Allocate memory */
dmlBuffer = (struct discHdr *)allocate_memory(sizeof(struct discHdr) * 1000);
if (!dmlBuffer)
return -1;
/* Clear buffer */
memset(dmlBuffer, 0, sizeof(struct discHdr) * 1000);
dml = get_DML_game_list(dmlBuffer);
channelCnt = get_channel_list_cnt();
/* Allocate memory */
channelBuffer = (struct discHdr *)allocate_memory(sizeof(struct discHdr) * channelCnt);
if (!channelBuffer)
return -1;
/* Clear buffer */
memset(channelBuffer, 0, sizeof(struct discHdr) * channelCnt);
s32 channelTemp = get_channel_list(channelBuffer, channelCnt);
dbg_printf("Channel: %d\n", channelTemp);
if (channelTemp > 0)
channelCnt = channelTemp;
else
channelCnt = 0;
/* Buffer length */
len = sizeof(struct discHdr) * (cnt+dml+channelCnt);
dbg_printf("Found %d games (%d wii games, %d gc games and %d channels)\n", len/sizeof(struct discHdr), cnt, dml, channelCnt);
/* Allocate memory */
buffer = (struct discHdr *)allocate_memory(len);
if (!buffer)
return -1;
/* Clear buffer */
memset(buffer, 0, len);
memcpy(buffer, dmlBuffer, sizeof(struct discHdr) * dml);
SAFE_FREE(dmlBuffer);
memcpy(buffer+dml, channelBuffer, sizeof(struct discHdr) * channelCnt);
SAFE_FREE(channelBuffer);
/* Get header list */
if (MountWBFS) {
ret = WBFS_GetHeaders(buffer+(dml+channelCnt), cnt, sizeof(struct discHdr));
if (ret < 0)
goto err;
}
/* Set values */
gameList = buffer;
gameCnt = cnt+dml+channelCnt;
/* Reset variables */
gameSelected = gameStart = 0;
/* Sort entries */
__set_default_sort();
sortList_default();
// hide and re-sort preffered
if (!CFG.admin_lock || CFG.admin_mode_locked) {
gameCnt = CFG_hide_games(gameList, gameCnt);
}
CFG_sort_pref(gameList, gameCnt);
// init favorites
all_gameList = gameList;
all_gameCnt = gameCnt;
len = sizeof(struct discHdr) * all_gameCnt;
fav_gameList = (struct discHdr *)allocate_memory(len);
if (fav_gameList) {
memcpy(fav_gameList, all_gameList, len);
fav_gameCnt = all_gameCnt;
}
filter_gameList = (struct discHdr *)allocate_memory(len);
if (filter_gameList) {
memcpy(filter_gameList, all_gameList, len);
filter_gameCnt = all_gameCnt;
}
return 0;
err:
/* Free memory */
SAFE_FREE(buffer);
return ret;
}
void Switch_Favorites(bool enable)
{
int i, len;
u8 *id = NULL;
// remember the selected game before we change the list
if (gameSelected < gameCnt) {
id = gameList[gameSelected].id;
}
// filter
if (fav_gameList) {
len = sizeof(struct discHdr) * all_gameCnt;
memcpy(fav_gameList, all_gameList, len);
fav_gameCnt = CFG_filter_favorite(fav_gameList, all_gameCnt);
}
// switch
//printf("fav %d %p %d\n", enable, fav_gameList, fav_gameCnt); sleep(5);
if (fav_gameList == NULL || fav_gameCnt == 0) enable = false;
if (enable) {
gameList = fav_gameList;
gameCnt = fav_gameCnt;
} else {
gameList = all_gameList;
gameCnt = all_gameCnt;
}
enable_favorite = enable;
// find game selected
gameStart = 0;
gameSelected = 0;
if (id) for (i=0; i<gameCnt; i++) {
if (strncmp((char*)gameList[i].id, (char*)id, 6) == 0) {
gameSelected = i;
break;
}
}
// scroll start list
__Menu_ScrollStartList();
}
char *__Menu_WrapTitle(char *title, int len)
{
//static char buffer[MAX_CHARACTERS + 4];
static char buffer[400];
memset(buffer, 0, sizeof(buffer));
STRCOPY(buffer, title);
/* if (len == 0) {
int rows;
CON_GetMetrics(&len, &rows);
len--;
}*/
con_wordwrap(buffer, len, sizeof(buffer));
// limit to 2 lines
char *ln = strchr(buffer, '\n');
if (ln) ln = strchr(ln+1, '\n');
if (ln) *ln = 0;
return buffer;
}
bool is_dual_layer(u64 real_size)
{
//return true; // dbg
u64 single = 4699979776LL;
if (real_size > single) return true;
return false;
}
int get_game_ios_idx(struct Game_CFG_2 *game_cfg)
{
if (game_cfg == NULL) {
return CURR_IOS_IDX;
}
return game_cfg->curr.ios_idx;
}
void warn_ios_bugs()
{
bool warn = false;
if (!is_ios_type(IOS_TYPE_WANIN)) {
CFG.patch_dvd_check = 1;
return;
}
// ios == 249
if (wbfs_part_fs && IOS_GetRevision() < 18) {
printf(
gt("ERROR: cIOS249rev18, cIOS222v4,\n"
"cIOS223v4, cIOS224v5 or higher required\n"
"for starting games from a FAT partition!\n"
"Upgrade IOS249 or choose a different IOS."));
printf("\n\n");
warn = true;
}
if (IOS_GetRevision() < 14) {
printf(
gt("NOTE: CIOS249 before rev14:\n"
"Possible error #001 is not handled!"));
printf("\n\n");
warn = true;
}
if (IOS_GetRevision() == 13) {
printf(
gt("NOTE: You are using CIOS249 rev13:\n"
"To quit the game you must reset or\n"
"power-off the Wii before you start\n"
"another game or it will hang here!"));
printf("\n\n");
warn = true;
}
if (IOS_GetRevision() < 12) {
CFG.patch_dvd_check = 1;
/*printf(
"NOTE: CIOS249 before rev12:\n"
"Any disc in the DVD drive is required!\n\n");
warn = true;*/
}
if (IOS_GetRevision() < 10
&& wbfsDev == WBFS_DEVICE_SDHC) {
printf(
gt("NOTE: CIOS249 before rev10:\n"
"Loading games from SDHC not supported!"));
printf("\n\n");
warn = true;
}
if (IOS_GetRevision() < 9
&& wbfsDev == WBFS_DEVICE_USB) {
printf(
gt("NOTE: CIOS249 before rev9:\n"
"Loading games from USB not supported!"));
printf("\n\n");
warn = true;
}
if (warn) sleep(1);
}
void print_dual_layer_note()
{
printf(
gt("NOTE: You are using CIOS249 rev14:\n"
"It has known problems with dual layer\n"
"games. It is highly recommended that\n"
"you use a different IOS or revision\n"
"for installation/playback of this game."));
printf("\n\n");
}
bool check_device(struct Game_CFG_2 *game_cfg, bool print)
{
int ii = get_game_ios_idx(game_cfg);
if (wbfs_part_fs == PART_FS_WBFS && wbfs_part_idx > 1) {
if (!is_ios_idx_mload(ii))
{
if (print) {
printf(
gt("ERROR: multiple wbfs partitions\n"
"supported only with IOS222/223-mload"));
printf("\n\n");
}
return false;
}
}
return true;
}
bool check_dual_layer(u64 real_size, struct Game_CFG_2 *game_cfg)
{
//return true; // dbg
if (!is_dual_layer(real_size)) return false;
if (game_cfg == NULL) {
if (is_ios_type(IOS_TYPE_WANIN) && (IOS_GetRevision() == 14)) return true;
return false;
}
if (get_ios_idx_type(game_cfg->curr.ios_idx) != IOS_TYPE_WANIN) return false;
if (is_ios_type(IOS_TYPE_WANIN)) {
if (IOS_GetRevision() == 14) return true;
return false;
}
// we are in 222 but cfg is 249, we can't know revision here
// so we'll just return false
return false;
}
void __Menu_GameSize(struct discHdr *header, u64 *comp_size, u64 *real_size)
{
static u64 last_comp = 0, last_real = 0;
static u8 last_id[8] = "";
//f32 size = 0.0;
*comp_size = 0;
*real_size = 0;
if (header->magic == CHANNEL_MAGIC) {
*comp_size = *real_size = getChannelSize(header);
return;
}
if (header->magic == GC_GAME_ON_DRIVE) {
*comp_size = *real_size = getDMLGameSize(header);
return;
}
/* Get game size */
if (strncmp((char*)last_id, (char*)header->id, 6) == 0 && last_comp && last_real) {
*comp_size = last_comp;
*real_size = last_real;
} else {
WBFS_GameSize2(header->id, comp_size, real_size);
last_comp = *comp_size;
last_real = *real_size;
strncpy((char*)last_id, (char*)header->id, 6);
}
}
void Menu_GameInfoStr2(struct discHdr *header, char *str, u64 comp_size, u64 real_size)
{
float size = (float)comp_size / 1024.0 / 1024.0 / 1024.0;
char *s = str;
sprintf(s, "[%.6s]", header->id);
s += strlen(s);
sprintf(s, " %.2f%s", size, gt("GB"));
if (is_dual_layer(real_size)) {
s += strlen(s);
sprintf(s, " %s", "(dual-layer)");
}
// req. ios
tmd game_tmd;
memset(&game_tmd, 0, sizeof(game_tmd));
if (header->magic == CHANNEL_MAGIC) {
game_tmd.sys_version = getChannelReqIos(header); //store channel ios directly in sys_version for compatability
}
else if (header->magic == GC_GAME_ON_DRIVE) {
//do nothing
} else {
wbfs_disc_t* d = WBFS_OpenDisc(header->id);
if (d) {
size = wbfs_extract_tmd(d, &game_tmd);
WBFS_CloseDisc(d);
}
}
if (game_tmd.sys_version) {
s += strlen(s);
sprintf(s, " IOS%d\n\n", TITLE_LOWER(game_tmd.sys_version));
} else {
s += strlen(s);
sprintf(s, "\n\n");
}
}
void Menu_GameInfoStr(struct discHdr *header, char *str)
{
u64 comp_size, real_size = 0;
__Menu_GameSize(header, &comp_size, &real_size);
Menu_GameInfoStr2(header, str, comp_size, real_size);
}
void __Menu_PrintInfo2(struct discHdr *header, u64 comp_size, u64 real_size)
{
char *title1;
//if (memcmp("G",header->id,1)==0 )
//title1 = header->title;
// if (strlen((char *)header->id)>6)
// title1=strcat(header->title,"(disc2)");
//else
title1 = get_title(header);
int len = con_len(title1);
int pad = con_len(CFG.menu_plus_s);
int cols, rows;
CON_GetMetrics(&cols, &rows);
if (pad + len < cols) {
printf_("%s\n", title1);
} else {
printf("%s\n", __Menu_WrapTitle(title1, cols-1));
}
char info_str[64];
Menu_GameInfoStr2(header, info_str, comp_size, real_size);
printf_("%s", info_str);
}
void __Menu_PrintInfo(struct discHdr *header)
{
u64 comp_size, real_size = 0;
__Menu_GameSize(header, &comp_size, &real_size);
__Menu_PrintInfo2(header, comp_size, real_size);
}
void __Menu_MoveList(s8 delta)
{
/* No game list */
if (!gameCnt)
return;
#if 0
/* Select next entry */
gameSelected += delta;
/* Out of the list? */
if (gameSelected <= -1)
gameSelected = (gameCnt - 1);
if (gameSelected >= gameCnt)
gameSelected = 0;
#endif
if(delta>0) {
if(gameSelected == gameCnt - 1) {
gameSelected = 0;
}
else {
gameSelected +=delta;
if(gameSelected >= gameCnt) {
gameSelected = (gameCnt - 1);
}
}
}
else {
if(!gameSelected) {
gameSelected = gameCnt - 1;
}
else {
gameSelected +=delta;
if(gameSelected < 0) {
gameSelected = 0;
}
}
}
/* List scrolling */
__Menu_ScrollStartList();
}
void __Menu_ScrollStartList()
{
s32 index = (gameSelected - gameStart);
if (index >= ENTRIES_PER_PAGE)
gameStart += index - (ENTRIES_PER_PAGE - 1);
if (index <= -1)
gameStart += index;
}
void __Menu_ShowList(void)
{
FgColor(CFG.color_header);
if (enable_favorite) {
printf_x(gt("Favorite Games"));
printf(":\n");
} else {
if (!CFG.hide_header) {
printf_x(gt("Select the game you want to boot"));
printf(":\n");
}
}
DefaultColor();
if (CFG.console_mark_page && gameStart > 0) {
printf(" %s +", CFG.cursor_space);
}
printf("\n");
/* No game list*/
if (gameCnt) {
u32 cnt;
/* Print game list */
for (cnt = gameStart; cnt < gameCnt; cnt++) {
struct discHdr *header = &gameList[cnt];
/* Entries per page limit reached */
if ((cnt - gameStart) >= ENTRIES_PER_PAGE)
break;
if (gameSelected == cnt) {
FgColor(CFG.color_selected_fg);
BgColor(CFG.color_selected_bg);
Con_ClearLine();
} else {
DefaultColor();
}
/* Print entry */
//printf(" %2s %s\n", (gameSelected == cnt) ? ">>" : " ",
char *title = __Menu_PrintTitle(get_title(header));
// cursor
printf(" %s", (gameSelected == cnt) ? CFG.cursor : CFG.cursor_space);
// favorite mark
printf("%s", (CFG.console_mark_favorite && is_favorite(header->id))
? CFG.favorite : " ");
// title
// if (memcmp("G",header->id,1)==0 )
// printf("%s",header->title);
// else
printf("%s",title);
// saved mark
if (CFG.console_mark_saved) {
printf("%*s", (MAX_CHARACTERS - con_len(title)),
(CFG_is_saved(header->id)) ? CFG.saved : " ");
}
printf("\n");
}
DefaultColor();
if (CFG.console_mark_page && cnt < gameCnt) {
printf(" %s +", CFG.cursor_space);
} else {
printf(" %s ", CFG.cursor_space);
}
//if (CFG.hide_hddinfo) {
FgColor(CFG.color_footer);
BgColor(CONSOLE_BG_COLOR);
int num_page = 1 + (gameCnt - 1) / ENTRIES_PER_PAGE;
int cur_page = 1 + gameSelected / ENTRIES_PER_PAGE;
printf(" %-*.*s %d/%d", MAX_CHARACTERS - 8, MAX_CHARACTERS - 8, action_string, cur_page, num_page);
if (!CFG.hide_hddinfo) printf("\n");
action_string[0] = 0;
//}
} else {
printf(" %s ", CFG.cursor);
printf("%s", gt("No games found!"));
printf("\n");
}
/* Print free/used space */
FgColor(CFG.color_footer);
BgColor(CONSOLE_BG_COLOR);
if (!CFG.hide_hddinfo) {
// Get free space
f32 free, used;
//printf("\n");
WBFS_DiskSpace(&used, &free);
printf_x(gt("Used: %.1fGB Free: %.1fGB [%d]"), used, free, gameCnt);
//int num_page = 1 + (gameCnt - 1) / ENTRIES_PER_PAGE;
//int cur_page = 1 + gameSelected / ENTRIES_PER_PAGE;
//printf(" %d/%d", cur_page, num_page);
}
if (!CFG.hide_footer) {
printf("\n");
// (B) GUI (1) Options (2) Favorites
// B: GUI 1: Options 2: Favorites
//char c_gui = 'B', c_opt = '1';
//if (CFG.buttons == CFG_BTN_OPTIONS_B) {
// c_gui = '1'; c_opt = 'B';
//}
printf_("");
if (CFG.gui && CFG.button_gui) {
printf(gt("%s: GUI "), (button_names[CFG.button_gui]));
}
if (!CFG.disable_options && CFG.button_opt) {
printf(gt("%s: Options "), (button_names[CFG.button_opt]));
}
if (CFG.button_fav) {
printf(gt("%s: Favorites"), (button_names[CFG.button_fav]));
}
}
if (CFG.db_show_info) {
printf("\n");
//load game info from XML - lustar
__Menu_ShowGameInfo(false, gameList[gameSelected].id);
}
DefaultColor();
__console_flush(0);
}
/* load game info from XML - lustar */
void __Menu_ShowGameInfo(bool showfullinfo, u8 *id)
{
if (LoadGameInfoFromXML(id)) {
FgColor(CFG.color_inactive);
PrintGameInfo();
if (showfullinfo) {
printf("\n");
PrintGameSynopsis();
}
//printf("Play Count: %d\n", getPlayCount(id));
DefaultColor();
}
}
void __Menu_ShowCover(void)
{
struct discHdr *header = &gameList[gameSelected];
/* No game list*/
if (!gameCnt)
return;
/* Draw cover */
Gui_DrawCover(header->id);
}
bool Save_ScreenShot(char *fn, int size)
{
int i;
struct stat st;
for (i=1; i<100; i++) {
snprintf(fn, size, "%s/screenshot%d.png", USBLOADER_PATH, i);
if (stat(fn, &st)) break;
}
return ScreenShot(fn);
}
void Make_ScreenShot()
{
bool ret;
char fn[200];
ret = Save_ScreenShot(fn, sizeof(fn));
printf("\n%s: %s\n", ret ? "Saved" : "Error Saving", fn);
sleep(1);
}
void Handle_Home()
{
if (CFG.home == CFG_HOME_EXIT) {
Con_Clear();
printf("\n");
printf_("Exiting...");
__console_flush(0);
Sys_Exit();
} else if (CFG.home == CFG_HOME_SCRSHOT) {
long long t1 = gettime();
while (Wpad_HeldButtons() & CFG.button_exit.mask) {
if (diff_msec(t1, gettime()) >= 1000) {
__console_flush(0);
Make_ScreenShot();
return;
}
VIDEO_WaitVSync();
}
// loop ended before timeout - run hbc
goto do_hbc;
} else if (CFG.home == CFG_HOME_HBC && CFG.disable_options == 0) {
do_hbc:
Con_Clear();
printf("\n");
printf_("HBC...");
__console_flush(0);
Sys_HBC();
} else if (CFG.home == CFG_HOME_REBOOT) {
Con_Clear();
Restart();
} else {
// Priiloader magic words, and channels
if ((CFG.home & 0xFF) < 'a') {
// upper case final letter implies channel
Con_Clear();
Sys_Channel(CFG.home);
} else {
// lower case final letter implies magic word
Con_Clear();
*(vu32*)0x8132FFFB = CFG.home;
Restart();
}
}
}
void Print_IOS_Info_str(char *str, int size)
{
*str = 0;
//int new_wanin = is_ios_type(IOS_TYPE_WANIN) && IOS_GetRevision() >= 18;
snprintf(str, size, "IOS%u (r%u)\n",
IOS_GetVersion(), IOS_GetRevision());
//CFG.ios_mload||new_wanin ? "[FRAG]" : "");
str_seek_end(&str, &size);
print_mload_version_str(str);
}
void Print_IOS_Info()
{
char str[200];
FgColor(CFG.color_inactive);
Print_IOS_Info_str(str, sizeof(str));
printf_("%s", str);
DefaultColor();
}
void Print_SYS_Info_str(char *str, int size, bool console)
{
snprintf(str, size, gt("Loader Version: %s"), CFG_VERSION);
str_seek_end(&str, &size);
snprintf(str, size, "\n");
str_seek_end(&str, &size);
snprintf(str, size, gt("CFG base: %s"), USBLOADER_PATH);
str_seek_end(&str, &size);
snprintf(str, size, "\n");
str_seek_end(&str, &size);
if (strcmp(LAST_CFG_PATH, USBLOADER_PATH)) {
// if last cfg differs, print it out
snprintf(str, size, "%s\n", gt("Additional config:"));
str_seek_end(&str, &size);
snprintf(str, size, " %s/config.txt\n", LAST_CFG_PATH);
str_seek_end(&str, &size);
}
MountPrint_str(str, size);
str_seek_end(&str, &size);
Print_IOS_Info_str(str, size);
if (!console) {
snprintf(str, size, "\n");
str_seek_end(&str, &size);
char devopath[200];
snprintf(devopath, sizeof(devopath), "%s/%s", USBLOADER_PATH, "loader.bin");
FILE *f = NULL;
f = fopen(devopath, "rb");
if (f) {
char buffer[72];
u32 i = 0;
fseek(f, 4, SEEK_SET);
fread(&buffer, 1, sizeof(buffer), f);
fclose(f);
for (i = 0; i < sizeof(buffer); i++) {
if (buffer[i] == '/' && i-5 > 0) {
buffer[i-5] = '\n';
break;
}
}
snprintf(str, size, buffer);
str_seek_end(&str, &size);
snprintf(str, size, "\n");
str_seek_end(&str, &size);
}
if (CFG.dml > 0) {
snprintf(str, size, DIOS_MIOS_INFO);
str_seek_end(&str, &size);
}
snprintf(str, size, gt("NAND Emu Path:\n%s\n"), CFG.nand_emu_path);
str_seek_end(&str, &size);
}
}
void Print_SYS_Info()
{
char str[400];
FgColor(CFG.color_inactive);
Print_SYS_Info_str(str, sizeof(str), true);
printf_("%s", str);
DefaultColor();
}
// WiiMote to char map for admin mode unlocking
char get_unlock_buttons(buttons)
{
switch (buttons)
{
case WPAD_BUTTON_UP: return 'U';
case WPAD_BUTTON_DOWN: return 'D';
case WPAD_BUTTON_LEFT: return 'L';
case WPAD_BUTTON_RIGHT: return 'R';
case WPAD_BUTTON_A: return 'A';
case WPAD_BUTTON_B: return 'B';
case WPAD_BUTTON_MINUS: return 'M';
case WPAD_BUTTON_PLUS: return 'P';
case WPAD_BUTTON_1: return '1';
case WPAD_BUTTON_2: return '2';
case WPAD_BUTTON_HOME: return 'H';
}
return 'x';
}
void Admin_Unlock(bool unlock)
{
if (unlock_init) {
// save original settings
disable_format = CFG.disable_format;
disable_remove = CFG.disable_remove;
disable_install = CFG.disable_install;
disable_options = CFG.disable_options;
confirm_start = CFG.confirm_start;
unlock_init = false;
}
if (unlock) {
// unlock
//enable all "admin-type" screens
CFG.disable_format = 0;
CFG.disable_remove = 0;
CFG.disable_install = 0;
CFG.disable_options = 0;
CFG.confirm_start = 1;
CFG.admin_mode_locked = 0;
} else {
//set the lock back on
CFG.disable_format = disable_format;
CFG.disable_remove = disable_remove;
CFG.disable_install = disable_install;
CFG.disable_options = disable_options;
CFG.confirm_start = confirm_start;
CFG.admin_mode_locked = 1;
}
// reset the hidden games
__Menu_GetEntries();
}
void Menu_Unlock() {
u32 buttons;
static long long t_start;
long long t_now;
unsigned ms_diff = 0;
int i = 0;
char buf[16];
bool unlocked = false;
memset(buf, 0, sizeof(buf));
Con_Clear();
printf(gt("Configurable Loader %s"), CFG_VERSION);
printf("\n\n");
if (CFG.admin_mode_locked) {
printf(gt("Enter Code: "));
t_start = gettime();
while (ms_diff < 30000 && !unlocked) {
buttons = Wpad_GetButtons();
if (buttons) {
printf("*");
buf[i] = get_unlock_buttons(buttons);
i++;
if (stricmp(buf, CFG.unlock_password) == 0) {
unlocked = true;
}
if (i >= 10) break;
}
VIDEO_WaitVSync();
t_now = gettime();
ms_diff = diff_msec(t_start, t_now);
}
}
Admin_Unlock(unlocked);
printf("\n\n");
if (unlocked) {
printf(gt("SUCCESS!"));
} else {
//set the lock back on
printf(gt("LOCKED!"));
}
sleep(1);
}
int Menu_Views()
{
struct discHdr *header = NULL;
int redraw_cover = 0;
struct Menu menu;
if (gameCnt) {
header = &gameList[gameSelected];
}
const int NUM_OPT = 8;
char active[NUM_OPT];
menu_init(&menu, NUM_OPT);
for (;;) {
menu_begin(&menu);
menu_init_active(&menu, active, sizeof(active));
if (CFG.disable_options) {
active[1] = 0;
active[2] = 0;
}
if (CFG.disable_remove) active[3] = 0;
if (CFG.disable_install) active[4] = 0;
Con_Clear();
FgColor(CFG.color_header);
printf_x(gt("Main Menu"));
printf(":\n\n");
DefaultColor();
MENU_MARK();
printf("<%s>\n", gt("Start Game"));
MENU_MARK();
printf("<%s>\n", gt("Game Options"));
MENU_MARK();
printf("<%s>\n", gt("Global Options"));
MENU_MARK();
printf("<%s>\n", gt("Delete Game"));
MENU_MARK();
printf("<%s>\n", gt("Install Game"));
MENU_MARK();
printf("<%s>\n", gt("Sort Games"));
MENU_MARK();
printf("<%s>\n", gt("Filter Games"));
MENU_MARK();
printf("<%s>\n", gt("Boot Disc"));
DefaultColor();
printf("\n");
printf_h(gt("Press %s button to select."),
(button_names[CFG.button_confirm.num]));
printf("\n");
DefaultColor();
__console_flush(0);
if (redraw_cover) {
if (header) Gui_DrawCover(header->id);
redraw_cover = 0;
}
u32 buttons = Wpad_WaitButtonsRpt();
menu_move_active(&menu, buttons);
int change = -2;
if (buttons & WPAD_BUTTON_LEFT) change = -1;
if (buttons & WPAD_BUTTON_RIGHT) change = +1;
if (buttons & CFG.button_confirm.mask) change = 0;
// #define CHANGE(V,M) {V+=change;if(V>M)V=M;if(V<0)V=0;}
if (change > -2) {
switch (menu.current) {
case 0:
CFG.confirm_start = 0;
Menu_Boot(0);
break;
case 1:
Menu_Game_Options();
break;
case 2:
Menu_Global_Options();
break;
case 3:
Menu_Remove();
break;
case 4:
Menu_Install();
break;
case 5:
Menu_Sort();
break;
case 6:
Menu_Filter();
break;
case 7:
Menu_Boot(1);
break;
}
}
// HOME button
if (buttons & CFG.button_exit.mask) {
Handle_Home();
}
if (buttons & CFG.button_cancel.mask) break;
}
return 0;
}
int Menu_Game_Options() {
return Menu_Boot_Options(&gameList[gameSelected], 0);
}
void Menu_Alt_Dol(struct discHdr *header, struct Game_CFG *game_cfg, int allow_back)
{
int i;
DOL_LIST dol_list;
struct Menu menu;
int num_opt;
int rows, cols, win_size;
CON_GetMetrics(&cols, &rows);
if ((win_size = rows-5) < 3) win_size = 3;
f32 size = 0.0;
WBFS_GameSize(header->id, &size);
WBFS_GetDolList(header->id, &dol_list);
load_dolmenu((char*)header->id);
int dm_num = dolmenubuffer ? dolmenubuffer[0].count : 0;
int dm_count = dm_num ? 1+dm_num : 0;
num_opt = ALT_DOL_DISC + 1 + dol_list.num + dm_count;
char active[num_opt];
menu_init(&menu, num_opt);
// find out current
if (game_cfg->alt_dol < ALT_DOL_DISC) {
menu.current = game_cfg->alt_dol;
} else if (game_cfg->alt_dol == ALT_DOL_DISC) {
if (allow_back)
menu.current = ALT_DOL_DISC;
else if (dm_num)
menu.current = ALT_DOL_DISC + 2 + dol_list.num;
else if (dol_list.num)
menu.current = ALT_DOL_DISC + 1;
else
menu.current = 0;
for (i=0; i<dol_list.num; i++) {
if (strcmp(game_cfg->dol_name, dol_list.name[i]) == 0) {
menu.current = ALT_DOL_DISC + 1 + i;
break;
}
}
} else {
// alt.dol plus
menu.current = dol_list.num + game_cfg->alt_dol + 1;
}
for (;;) {
menu_init_active(&menu, active, sizeof(active));
active[ALT_DOL_DISC + 1 + dol_list.num] = 0;
if (!allow_back) active[ALT_DOL_DISC] = 0;
Con_Clear();
FgColor(CFG.color_header);
printf_x(gt("Selected Game"));
printf(":");
printf(" (%.6s) (%.2f%s)\n", header->id, size, gt("GB"));
DefaultColor();
printf(" %s %s\n", CFG.cursor_space, __Menu_PrintTitle(get_title(header)));
FgColor(CFG.color_header);
printf_x(gt("Launch Methods"));
printf(":\n");
DefaultColor();
menu_begin(&menu);
menu_window_begin(&menu, win_size, num_opt);
if (menu_window_mark(&menu)) {
if (allow_back)
printf("%s\n", gt("Off"));
else
printf("main.dol\n");
}
if (menu_window_mark(&menu))
printf("SD\n");
if (menu_window_mark(&menu)) {
if (allow_back)
printf("%s\n", gt("Disc (Ask)"));
else
printf("%s\n", gt ("Disc"));
}
for (i=0; i<dol_list.num; i++) {
if (menu_window_mark(&menu))
printf(" %s\n", dol_list.name[i]);
}
DefaultColor();
if (dm_num) {
if (menu_window_mark(&menu))
printf("Disc Plus:\n");
for (i=0; i<dm_num; i++) {
if (menu_window_mark(&menu))
printf(" %s\n", dolmenubuffer[i+1].name);
}
}
DefaultColor();
menu_window_end(&menu, cols);
printf_h(gt("Press %s button to select."),
(button_names[CFG.button_confirm.num]));
if (allow_back) {
printf("\n");
printf_h(gt("Press %s button to go back."),
(button_names[CFG.button_cancel.num]));
}
__console_flush(0);
u32 buttons = Wpad_WaitButtonsRpt();
int change = 0;
menu_move_active(&menu, buttons);
if (buttons & CFG.button_exit.mask) Handle_Home();
if ((buttons & CFG.button_cancel.mask) && allow_back) return;
if (buttons & CFG.button_confirm.mask) change = 1;
// if (buttons & WPAD_BUTTON_LEFT) change = 1;
// if (buttons & WPAD_BUTTON_RIGHT) change = 1;
if (change) {
if (menu.current <= ALT_DOL_DISC) {
game_cfg->alt_dol = menu.current;
*game_cfg->dol_name = 0;
return;
}
i = menu.current - ALT_DOL_DISC -1;
if (i < dol_list.num) {
game_cfg->alt_dol = ALT_DOL_DISC;
STRCOPY(game_cfg->dol_name, dol_list.name[i]);
//printf("DN: %s\n", game_cfg->dol_name); sleep(3);
return;
}
i = menu.current - ALT_DOL_PLUS - dol_list.num - 1;
game_cfg->alt_dol = ALT_DOL_PLUS + i;
STRCOPY(game_cfg->dol_name, dolmenubuffer[i+1].dolname);
return;
}
}
}
int Menu_Boot_Options(struct discHdr *header, bool disc) {
int ret_val = 0;
if (CFG.disable_options) return 0;
if (!gameCnt && !disc) {
// if no games, go directly to global menu
return 1;
}
struct Game_CFG_2 *game_cfg2 = NULL;
struct Game_CFG *game_cfg = NULL;
int opt_saved;
//int opt_ios_reload;
int opt_language, opt_video, opt_video_patch, opt_vidtv, opt_padhook, opt_nand_emu;
int opt_mem_card_emu, opt_mem_card_size, opt_country_patch, opt_anti_002, opt_ocarina, opt_wide_screen, opt_nodisc, opt_ntsc_j_patch, opt_screenshot, opt_private_server;
f32 size = 0.0;
int redraw_cover = 0;
int i;
int rows, cols, win_size;
CON_GetMetrics(&cols, &rows);
if ((win_size = rows-9) < 3) win_size = 3;
Con_Clear();
FgColor(CFG.color_header);
printf_x(gt("Selected Game"));
printf(":");
__console_flush(0);
if (disc) {
printf(" (%.6s)\n", header->id);
} else {
if (header->magic == GC_GAME_ON_DRIVE)
{
u64 tempSize = getDMLGameSize(header);
if (tempSize > 0) {
size = (float)tempSize / 1024.0 / 1024.0 / 1024.0;
}
}
else if (header->magic == CHANNEL_MAGIC) {
size = (float)getChannelSize(header) / 1024.0 / 1024.0 / 1024.0;
}
else
{
WBFS_GameSize(header->id, &size);
}
printf(" (%.6s) (%.2f%s)\n", header->id, size, gt("GB"));
}
DefaultColor();
printf(" %s %s\n\n", CFG.cursor_space, __Menu_PrintTitle(get_title(header)));
__console_flush(0);
load_dolmenu((char*)header->id);
game_cfg2 = CFG_get_game(header->id);
if (!game_cfg2) {
printf(gt("ERROR game opt"));
printf("\n");
sleep(5);
return 0;
}
game_cfg = &game_cfg2->curr;
struct Menu menu;
int NUM_OPT = 20;
if (header->magic == GC_GAME_ON_DRIVE) NUM_OPT = 18;
if (header->magic == CHANNEL_MAGIC) NUM_OPT = 19;
char active[NUM_OPT];
menu_init(&menu, NUM_OPT);
for (;;) {
/*
// fat on 249?
if (wbfs_part_fs && !disc) {
if (!is_ios_idx_mload(game_cfg->ios_idx))
{
game_cfg->ios_idx = CFG_IOS_222_MLOAD;
}
}
*/
menu_init_active(&menu, active, sizeof(active));
opt_saved = game_cfg2->is_saved;
// block ios reload is supported with hermes and d2x
// old: if not mload disable block ios reload opt
/*
opt_ios_reload = game_cfg->block_ios_reload;
if (!is_ios_idx_mload(game_cfg->ios_idx)) {
active[8] = 0;
opt_ios_reload = 0;
}
*/
// clean options
opt_language = game_cfg->language;
opt_video = game_cfg->video;
opt_video_patch = game_cfg->video_patch;
opt_vidtv = game_cfg->vidtv;
opt_country_patch = game_cfg->country_patch;
opt_mem_card_emu = game_cfg->mem_card_emu;
opt_mem_card_size = game_cfg->mem_card_size;
opt_anti_002 = game_cfg->fix_002;
opt_wide_screen = game_cfg->wide_screen;
opt_nodisc = game_cfg->nodisc;
opt_screenshot = game_cfg->screenshot;
opt_padhook = game_cfg->hooktype;
opt_ocarina = game_cfg->ocarina;
opt_ntsc_j_patch = game_cfg->ntsc_j_patch;
opt_nand_emu = game_cfg->nand_emu;
opt_private_server = game_cfg->private_server;
if (game_cfg->clean == CFG_CLEAN_ALL) {
opt_language = CFG_LANG_CONSOLE;
opt_video = CFG_VIDEO_GAME;
opt_video_patch = CFG_VIDEO_PATCH_OFF;
opt_vidtv = 0;
opt_country_patch = 0;
opt_mem_card_emu = 0;
opt_mem_card_size = 2;
opt_anti_002 = 0;
opt_ocarina = 0;
opt_ntsc_j_patch = 0;
opt_nand_emu = 0;
opt_nodisc = 0;
opt_screenshot = 0;
opt_private_server = 0;
active[1] = 0; // language
active[2] = 0; // video
active[3] = 0; // video_patch
active[4] = 0; // vidtv
active[5] = 0; // country
active[6] = 0; // anti 002
active[10] = 0; // ocarina
active[11] = 0; // hook
}
// if not ocarina and not wiird, deactivate hooks
//if (!game_cfg->ocarina && !CFG.wiird) {
// active[11] = 0;
//}
//if admin lock is off or they're not in admin
// mode then they can't hide/unhide games
// if (!CFG.admin_lock || CFG.admin_mode_locked) {
// active[14] = 0;
// }
//These things shouldn't be changed if using a disc...maybe
// if ((disc) && memcmp("G",header->id,1)==0) {
//active[3] = 0;
// active[8] = 0;
//active[9] = 0;
//active[10] = 0;
// }
Con_Clear();
FgColor(CFG.color_header);
printf_x(gt("Selected Game"));
printf(":");
if (disc) printf(" (%.6s)\n", header->id);
else printf(" (%.6s) (%.2fGB)\n", header->id, size);
DefaultColor();
printf(" %s %s\n\n", CFG.cursor_space, __Menu_PrintTitle(get_title(header)));
FgColor(CFG.color_header);
printf_x(gt("Game Options: %s"),
CFG_is_changed(header->id) ? gt("[ CHANGED ]") :
opt_saved ? gt("[ SAVED ]") : "");
printf("\n");
DefaultColor();
char c1 = '<', c2 = '>';
//if (opt_saved) { c1 = '['; c2 = ']'; }
const char *str_alt_dol = gt("Off");
if (!disc) {
if (game_cfg->alt_dol == ALT_DOL_OFF) {
str_alt_dol = gt("Off");
} else if (game_cfg->alt_dol == ALT_DOL_SD) {
str_alt_dol = "SD";
} else if (game_cfg->alt_dol == ALT_DOL_DISC) {
str_alt_dol = "Disc";
if (*game_cfg->dol_name) str_alt_dol = game_cfg->dol_name;
} else {
// alt.dol plus
i = game_cfg->alt_dol - ALT_DOL_PLUS;
str_alt_dol = dolmenubuffer[i+1].name;
}
}
// start menu draw
menu_begin(&menu);
menu_jump_active(&menu);
#define PRINT_OPT_S(N,V) \
printf("%s%c %s %c\n", con_align(N,18), c1, V, c2)
#define PRINT_OPT_A(N,V) \
printf("%s%c%s%c\n", con_align(N,18), c1, V, c2)
#define PRINT_OPT_B(N,V) \
PRINT_OPT_S(N,(V?gt("On"):gt("Off")))
if (header->magic == GC_GAME_ON_DRIVE) {
int num_gc_boot = map_get_num(map_gc_boot);
char *names_gc_boot[num_gc_boot];
num_gc_boot = map_to_list(map_gc_boot, num_gc_boot, names_gc_boot);
menu_window_begin(&menu, win_size, NUM_OPT);
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Favorite:"), is_favorite(header->id) ? gt("Yes") : gt("No"));
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Language:"), gt(languages[opt_language]));
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Video:"), gt(DML_videos[opt_video]));
if (menu_window_mark(&menu)) {
if (game_cfg->block_ios_reload == 2) {
PRINT_OPT_S(gt("Video Patch:"), gt("Auto"));
} else {
PRINT_OPT_B(gt("Video Patch:"), game_cfg->block_ios_reload);
}
}
if (menu_window_mark(&menu))
PRINT_OPT_B(gt("LED:"), opt_vidtv);
if (menu_window_mark(&menu)) {
if (opt_mem_card_emu == 2) {
PRINT_OPT_S(gt("NMM:"), gt("Shared"));
} else {
PRINT_OPT_B(gt("NMM:"), opt_mem_card_emu);
}
}
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("MC size::"), gt(str_mem_card_size[opt_mem_card_size]));
if (menu_window_mark(&menu))
PRINT_OPT_B(gt("Ocarina (cheats):"), opt_ocarina);
if (menu_window_mark(&menu))
PRINT_OPT_A(gt("Cheat Codes:"), gt("Manage"));
if (menu_window_mark(&menu))
printf("%s%s\n", con_align(gt("Cover Image:"), 18),
imageNotFound ? gt("< DOWNLOAD >") : gt("[ FOUND ]"));
if (menu_window_mark(&menu))
PRINT_OPT_B(gt("Wide Screen:"), opt_wide_screen);
if (menu_window_mark(&menu))
PRINT_OPT_B(gt("NTSC-J patch:"), opt_ntsc_j_patch);
if (menu_window_mark(&menu))
PRINT_OPT_B(gt("NoDisc:"), opt_nodisc);
if (menu_window_mark(&menu))
PRINT_OPT_B(gt("PAD HOOK:"), opt_padhook);
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Boot method:"), gt(names_gc_boot[game_cfg->channel_boot]));
if (menu_window_mark(&menu))
PRINT_OPT_B(gt("Screenshot:"), opt_screenshot);
if (menu_window_mark(&menu))
PRINT_OPT_B(gt("Alt Button Cfg:"), game_cfg->alt_controller_cfg);
if (menu_window_mark(&menu))
PRINT_OPT_B(gt("Remove Speed Limit:"), game_cfg->rem_speed_limit);
/*
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Write Playlog:"), gt(playlog_name[game_cfg->write_playlog]));
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Clear Patches:"), gt(names_vpatch[game_cfg->clean]));
*/
} else if (header->magic == CHANNEL_MAGIC) {
int num_channel_boot = map_get_num(map_channel_boot);
char *names_channel_boot[num_channel_boot];
num_channel_boot = map_to_list(map_channel_boot, num_channel_boot, names_channel_boot);
menu_window_begin(&menu, win_size, NUM_OPT);
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Favorite:"), is_favorite(header->id) ? gt("Yes") : gt("No"));
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Language:"), gt(languages[opt_language]));
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Video:"), gt(videos[opt_video]));
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Video Patch:"), gt(names_vpatch[opt_video_patch]));
if (menu_window_mark(&menu))
PRINT_OPT_B("VIDTV:", opt_vidtv);
if (menu_window_mark(&menu))
PRINT_OPT_B(gt("Country Fix:"), opt_country_patch);
if (menu_window_mark(&menu))
PRINT_OPT_B(gt("Anti 002 Fix:"), opt_anti_002);
if (menu_window_mark(&menu))
PRINT_OPT_S("IOS:", ios_str(game_cfg->ios_idx));
if (menu_window_mark(&menu)) {
if (game_cfg->block_ios_reload == 2) {
PRINT_OPT_S(gt("Block IOS Reload:"), gt("Auto"));
} else {
PRINT_OPT_B(gt("Block IOS Reload:"), game_cfg->block_ios_reload);
}
}
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Alt dol:"), str_alt_dol);
if (menu_window_mark(&menu))
PRINT_OPT_B(gt("Ocarina (cheats):"), opt_ocarina);
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Hook Type:"), hook_name[game_cfg->hooktype]);
if (menu_window_mark(&menu))
PRINT_OPT_A(gt("Cheat Codes:"), gt("Manage"));
if (menu_window_mark(&menu))
printf("%s%s\n", con_align(gt("Cover Image:"), 18),
imageNotFound ? gt("< DOWNLOAD >") : gt("[ FOUND ]"));
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Hide Game:"), is_hide_game(header->id) ? gt("Yes") : gt("No"));
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Write Playlog:"), gt(playlog_name[game_cfg->write_playlog]));
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Clear Patches:"), gt(names_vpatch[game_cfg->clean]));
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Plugin:"), gt(names_channel_boot[game_cfg->channel_boot]));
if (menu_window_mark(&menu))
PRINT_OPT_A(gt("Savegame:"), gt("Dump savegame"));
} else {
menu_window_begin(&menu, win_size, NUM_OPT);
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Favorite:"), is_favorite(header->id) ? gt("Yes") : gt("No"));
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Language:"), gt(languages[opt_language]));
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Video:"), gt(videos[opt_video]));
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Video Patch:"), gt(names_vpatch[opt_video_patch]));
if (menu_window_mark(&menu))
PRINT_OPT_B("VIDTV:", opt_vidtv);
if (menu_window_mark(&menu))
PRINT_OPT_B(gt("Country Fix:"), opt_country_patch);
if (menu_window_mark(&menu))
PRINT_OPT_B(gt("Anti 002 Fix:"), opt_anti_002);
if (menu_window_mark(&menu))
PRINT_OPT_S("IOS:", ios_str(game_cfg->ios_idx));
if (menu_window_mark(&menu)) {
if (game_cfg->block_ios_reload == 2) {
PRINT_OPT_S(gt("Block IOS Reload:"), gt("Auto"));
} else {
PRINT_OPT_B(gt("Block IOS Reload:"), game_cfg->block_ios_reload);
}
}
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Alt dol:"), str_alt_dol);
if (menu_window_mark(&menu))
PRINT_OPT_B(gt("Ocarina (cheats):"), opt_ocarina);
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Hook Type:"), hook_name[game_cfg->hooktype]);
if (menu_window_mark(&menu))
PRINT_OPT_A(gt("Cheat Codes:"), gt("Manage"));
if (menu_window_mark(&menu))
printf("%s%s\n", con_align(gt("Cover Image:"), 18),
imageNotFound ? gt("< DOWNLOAD >") : gt("[ FOUND ]"));
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Hide Game:"), is_hide_game(header->id) ? gt("Yes") : gt("No"));
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Write Playlog:"), gt(playlog_name[game_cfg->write_playlog]));
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("Clear Patches:"), gt(names_vpatch[game_cfg->clean]));
if (menu_window_mark(&menu))
PRINT_OPT_S(gt("NAND Emu:"), gt(str_nand_emu[opt_nand_emu]));
if (menu_window_mark(&menu))
PRINT_OPT_A(gt("Savegame:"), gt("Dump savegame"));
if (menu_window_mark(&menu))
PRINT_OPT_A(gt("Private server:"), gt(str_private_server[opt_private_server]));
}
DefaultColor();
menu_window_end(&menu, cols);
printf_h(gt("Press %s to start game"), (button_names[CFG.button_confirm.num]));
printf("\n");
bool need_save = !opt_saved || CFG_is_changed(header->id);
if (need_save)
printf_h(gt("Press %s to save options"), (button_names[CFG.button_save.num]));
else
printf_h(gt("Press %s to discard options"), (button_names[CFG.button_save.num]));
printf("\n");
printf_h(gt("Press %s for global options"), (button_names[CFG.button_other.num]));
DefaultColor();
__console_flush(0);
if (redraw_cover) {
Gui_DrawCover(header->id);
redraw_cover = 0;
}
u32 buttons = Wpad_WaitButtonsRpt();
int change = 0;
menu_move_active(&menu, buttons);
if (buttons & WPAD_BUTTON_LEFT) change = -1;
if (buttons & WPAD_BUTTON_RIGHT) change = +1;
if (change && ((header->magic == GC_GAME_ON_DRIVE) || header->magic == GC_MAGIC)) {
switch (menu.current) {
case 0:
printf("\n\n");
printf_x(gt("Saving Settings... "));
__console_flush(0);
if (set_favorite(header->id, change > 0)) {
printf(gt("OK"));
} else {
printf(gt("ERROR"));
sleep(1);
}
__console_flush(0);
Gui_DrawCover(header->id);
break;
case 1:
CHANGE(game_cfg->language, CFG_LANG_MAX);
break;
case 2:
CHANGE(game_cfg->video, 5);
break;
case 3:
CHANGE(game_cfg->block_ios_reload, 2);
break;
case 4:
CHANGE(game_cfg->vidtv, 1);
break;
case 5:
CHANGE(game_cfg->mem_card_emu, 2);
break;
case 6:
CHANGE(game_cfg->mem_card_size, 5);
break;
case 7:
CHANGE(game_cfg->ocarina, 1);
break;
case 8:
Menu_Cheats(header);
break;
case 9:
printf("\n\n");
Download_Cover((char*)header->id, change > 0, true);
Cache_Invalidate();
Gui_DrawCover(header->id);
Menu_PrintWait();
break;
case 10: // Wide Screen
CHANGE(game_cfg->wide_screen, 1);
break;
case 11: // NoDisc+
CHANGE(game_cfg->ntsc_j_patch, 1);
break;
case 12: // NoDisc+
CHANGE(game_cfg->nodisc, 1);
break;
case 13: // PAD HOOK)
CHANGE(game_cfg->hooktype, 1);
break;
case 14: // gc Boot Method
CHANGE(game_cfg->channel_boot, 3);
break;
case 15: // Screenshot
CHANGE(game_cfg->screenshot, 1);
break;
case 16: // Alt Button Cfg
CHANGE(game_cfg->alt_controller_cfg, 1);
break;
case 17: // Remove Speed Limit
CHANGE(game_cfg->rem_speed_limit, 1);
break;
}
} else if (change && (header->magic == CHANNEL_MAGIC)) {
switch (menu.current) {
case 0:
printf("\n\n");
printf_x(gt("Saving Settings... "));
__console_flush(0);
if (set_favorite(header->id, change > 0)) {
printf(gt("OK"));
} else {
printf(gt("ERROR"));
sleep(1);
}
__console_flush(0);
Gui_DrawCover(header->id);
break;
case 1:
CHANGE(game_cfg->language, CFG_LANG_MAX);
break;
case 2:
CHANGE(game_cfg->video, CFG_VIDEO_MAX);
break;
case 3:
CHANGE(game_cfg->video_patch, CFG_VIDEO_PATCH_MAX);
break;
case 4:
CHANGE(game_cfg->vidtv, 1);
break;
case 5:
CHANGE(game_cfg->country_patch, 1);
break;
case 6:
CHANGE(game_cfg->fix_002, 1);
break;
case 7:
CHANGE(game_cfg->ios_idx, CFG_IOS_MAX);
break;
case 8:
CHANGE(game_cfg->block_ios_reload, 2);
break;
case 9:
if (!disc) Menu_Alt_Dol(header, game_cfg, 1);
break;
case 10:
CHANGE(game_cfg->ocarina, 1);
break;
case 11:
CHANGE(game_cfg->hooktype, NUM_HOOK-1);
break;
case 12:
Menu_Cheats(header);
break;
case 13:
printf("\n\n");
Download_Cover((char*)header->id, change > 0, true);
Cache_Invalidate();
Gui_DrawCover(header->id);
Menu_PrintWait();
break;
case 14: // hide game
printf("\n\n");
printf_x(gt("Saving Settings... "));
__console_flush(0);
if (set_hide_game(header->id, change > 0)) {
printf(gt("OK"));
} else {
printf(gt("ERROR"));
sleep(1);
}
__console_flush(0);
Gui_DrawCover(header->id);
break;
case 15:
CHANGE(game_cfg->write_playlog, 3);
break;
case 16:
CHANGE(game_cfg->clean, 2);
break;
case 17:
CHANGE(game_cfg->channel_boot, 1);
break;
case 18:
Menu_dump_savegame(header);
break;
}
} else if (change) {
switch (menu.current) {
case 0:
printf("\n\n");
printf_x(gt("Saving Settings... "));
__console_flush(0);
if (set_favorite(header->id, change > 0)) {
printf(gt("OK"));
} else {
printf(gt("ERROR"));
sleep(1);
}
__console_flush(0);
Gui_DrawCover(header->id);
break;
case 1:
CHANGE(game_cfg->language, CFG_LANG_MAX);
break;
case 2:
CHANGE(game_cfg->video, CFG_VIDEO_MAX);
break;
case 3:
CHANGE(game_cfg->video_patch, CFG_VIDEO_PATCH_MAX);
break;
case 4:
CHANGE(game_cfg->vidtv, 1);
break;
case 5:
CHANGE(game_cfg->country_patch, 1);
break;
case 6:
CHANGE(game_cfg->fix_002, 1);
break;
case 7:
CHANGE(game_cfg->ios_idx, CFG_IOS_MAX);
break;
case 8:
CHANGE(game_cfg->block_ios_reload, 2);
break;
case 9:
if (!disc) Menu_Alt_Dol(header, game_cfg, 1);
break;
case 10:
CHANGE(game_cfg->ocarina, 1);
break;
case 11:
CHANGE(game_cfg->hooktype, NUM_HOOK-1);
break;
case 12:
Menu_Cheats(header);
break;
case 13:
printf("\n\n");
Download_Cover((char*)header->id, change > 0, true);
Cache_Invalidate();
Gui_DrawCover(header->id);
Menu_PrintWait();
break;
case 14: // hide game
printf("\n\n");
printf_x(gt("Saving Settings... "));
__console_flush(0);
if (set_hide_game(header->id, change > 0)) {
printf(gt("OK"));
} else {
printf(gt("ERROR"));
sleep(1);
}
__console_flush(0);
Gui_DrawCover(header->id);
break;
case 15:
CHANGE(game_cfg->write_playlog, 3);
break;
case 16:
CHANGE(game_cfg->clean, 2);
break;
case 17:
CHANGE(game_cfg->nand_emu, 2);
break;
case 18:
Menu_dump_savegame(header);
break;
case 19:
CHANGE(game_cfg->private_server, 3);
break;
}
}
if (buttons & CFG.button_confirm.mask) {
CFG.confirm_start = 0;
Music_Pause();
Menu_Boot(disc);
break;
}
if (buttons & CFG.button_save.mask) {
bool ret;
printf("\n\n");
if (need_save) {
ret = CFG_save_game_opt(header->id);
if (ret) {
printf_x(gt("Options saved for this game."));
} else printf(gt("Error saving options!"));
} else {
ret = CFG_discard_game_opt(header->id);
if (ret) printf_x(gt("Options discarded for this game."));
else printf(gt("Error discarding options!"));
}
sleep(1);
}
// HOME button
if (buttons & CFG.button_exit.mask) {
Handle_Home();
}
if (buttons & CFG.button_other.mask) { ret_val = 1; break; }
if (buttons & CFG.button_cancel.mask) break;
}
CFG_release_game(game_cfg2);
// refresh favorites list
if (!disc) Switch_Favorites(enable_favorite);
return ret_val;
}
void Save_Game_List()
{
char name[200];
FILE *f;
int i;
printf_x(gt("Saving gamelist.txt ... "));
__console_flush(0);
snprintf(D_S(name), "%s/%s", USBLOADER_PATH, "gamelist.txt");
f = fopen(name, "wb");
if (!f) goto error;
fprintf(f, "# CFG USB Loader game list %s\n", CFG_VERSION);
for (i=0; i<all_gameCnt; i++) {
fprintf(f, "# %.6s %s\n", all_gameList[i].id, all_gameList[i].title);
fprintf(f, "%.6s = %s\n", all_gameList[i].id, get_title(&all_gameList[i]));
}
fclose(f);
printf("OK");
return;
error:
printf(gt("ERROR"));
}
void print_debug_hdr(char *str, int size)
{
snprintf(str, size, "# CFG USB Loader %s\n", CFG_VERSION);
str_seek_end(&str, &size);
snprintf(str, size, "\nIOS:\n\n");
str_seek_end(&str, &size);
print_all_ios_info_str(str, size);
str_seek_end(&str, &size);
snprintf(str, size, "\nMEM STATS:\n");
str_seek_end(&str, &size);
lib_mem_stat_str(str, size);
str_seek_end(&str, &size);
snprintf(str, size, "\nTIME STATS:\n\n");
str_seek_end(&str, &size);
time_statsf(NULL, str, size);
str_seek_end(&str, &size);
snprintf(str, size, "\nDEBUG LOG:\n\n");
str_seek_end(&str, &size);
}
void Save_Debug()
{
char *name = "debug.log";
char path[200];
char str[2000];
FILE *f;
printf("\n\n");
snprintf(D_S(path), "%s/%s", USBLOADER_PATH, name);
printf_x("%s\n%s\n", gt("Saving:"), path);
__console_flush(0);
f = fopen(path, "w");
if (!f) {
printf_(gt("ERROR"));
return;
}
print_debug_hdr(str, sizeof(str));
fprintf(f, "%s", str);
fprintf(f, "%s\n", dbg_log_buf);
fprintf(f, "\nEND\n");
fclose(f);
printf_("OK");
}
void Save_IOS_Hash()
{
char *name = "ioshash.txt";
char path[200];
FILE *f;
printf("\n\n");
snprintf(D_S(path), "%s/%s", USBLOADER_PATH, name);
printf_x("%s\n%s\n", gt("Saving:"), path);
__console_flush(0);
f = fopen(path, "a"); // append
if (!f) {
printf_(gt("ERROR"));
return;
}
time_t t = time(NULL);
fprintf(f, "\n# CFG %s %s", CFG_VERSION, ctime(&t));
char *info = get_ios_info_from_tmd();
if (!info) info = "??";
fprintf(f, "IOS%d, ", IOS_GetVersion());
char str[100];
if (get_ios_tmd_hash_str(str)) {
fprintf(f, "%s", str);
} else {
fprintf(f, "TMD HASH ERROR");
}
fprintf(f, " r%d Base: %s\n\n", IOS_GetRevision(), info);
fclose(f);
printf_("OK");
}
void Menu_Save_Settings()
{
int ret;
printf("\n");
printf_x(gt("Saving Settings... "));
printf("\n");
__console_flush(0);
FgColor(CFG.color_inactive);
ret = CFG_Save_Global_Settings();
DefaultColor();
if (ret) {
printf_(gt("OK"));
printf("\n");
Save_Game_List();
} else {
printf_(gt("ERROR"));
}
printf("\n");
//sleep(2);
printf_(gt("NOTE: may loader restart is required\nfor the settings to take effect."));
printf("\n\n");
Menu_PrintWait();
}
void Menu_All_IOS_Info()
{
printf("\n\ncIOS Info:\n\n");
print_all_ios_info(stdout);
printf("\n");
Menu_PrintWait();
}
int Menu_Global_Options()
{
int rows, cols, win_size, info_size;
CON_GetMetrics(&cols, &rows);
//calc_rows: // debug
info_size = 11;
if (strcmp(LAST_CFG_PATH, USBLOADER_PATH)) info_size += 2;
win_size = rows - info_size;
if (win_size < 3) win_size = 3;
if (CFG.disable_options) return 0;
struct discHdr *header = NULL;
int redraw_cover = 0;
struct Menu menu;
const int num_opt = 14;
char active[num_opt];
menu_init(&menu, num_opt);
menu_init_active(&menu, active, sizeof(active));
active[5] = usb_isgeckoalive(EXI_CHANNEL_1);
for (;;) {
menu_begin(&menu);
if (gameCnt) {
header = &gameList[gameSelected];
} else {
header = NULL;
}
Con_Clear();
FgColor(CFG.color_header);
printf_x(gt("Global Options"));
printf(":\n");
DefaultColor();
menu_window_begin(&menu, win_size, num_opt);
if (menu_window_mark(&menu))
printf("<%s>\n", gt("Main Menu"));
if (menu_window_mark(&menu))
printf("%s%2d/%-2d< %s > (%d)\n", con_align(gt("Profile:"),8),
CFG.current_profile + 1, CFG.num_profiles,
CFG.profile_names[CFG.current_profile],
CFG.num_favorite_game);
if (menu_window_mark(&menu))
printf("%s%2d/%2d < %s >\n", con_align(gt("Theme:"),7),
cur_theme + 1, num_theme, *CFG.theme ? CFG.theme : gt("none"));
if (menu_window_mark(&menu))
printf("%s< %s >\n", con_align(gt("Device:"),13),
(wbfsDev == WBFS_DEVICE_USB) ? "USB" : "SDHC");
if (menu_window_mark(&menu))
printf("%s< %s >\n", con_align(gt("Partition:"),13), CFG.partition);
if (menu_window_mark(&menu))
printf("%s< %s >\n", con_align("WiiRD:",13), gt(str_wiird[CFG.wiird]));
if (menu_window_mark(&menu))
printf("%s< %s >\n", con_align(gt("Default Gamecube Loader:"),24), gt(map_get_name(map_gc_boot, CFG.default_gc_loader+1)));
if (menu_window_mark(&menu))
printf("<%s>\n", gt("Download All Missing Covers"));
if (menu_window_mark(&menu))
printf("<%s>\n", gt("Download game information")); // download database - lustar
if (menu_window_mark(&menu))
printf("<%s>\n", gt("Download Plugins"));
if (menu_window_mark(&menu))
printf("<%s>\n", gt("Update themes"));
if (menu_window_mark(&menu))
printf("<%s>\n", gt("Check For Updates"));
if (menu_window_mark(&menu))
printf("<%s>\n", gt("Save debug.log"));
if (menu_window_mark(&menu))
printf("<%s>\n", gt("Show cIOS info"));
DefaultColor();
menu_window_end(&menu, cols);
printf_h(gt("Press %s for game options"), (button_names[CFG.button_other.num]));
printf("\n");
printf_h(gt("Press %s to save global settings"), (button_names[CFG.button_save.num]));
printf("\n\n");
Print_SYS_Info();
DefaultColor();
//Con_SetPosition(0, rows-1); printf(" -- %d %d --", rows, win_size); //calc_rows debug
__console_flush(0);
if (redraw_cover) {
if (header) Gui_DrawCover(header->id);
redraw_cover = 0;
}
u32 buttons = Wpad_WaitButtonsRpt();
menu_move(&menu, buttons);
int change = 0;
if (buttons & WPAD_BUTTON_LEFT) change = -1;
if (buttons & WPAD_BUTTON_RIGHT) change = +1;
if (buttons & CFG.button_confirm.mask) change = +1;
if (change) {
switch (menu.current) {
case 0:
Menu_Views();
return 0;
case 1:
CHANGE(CFG.current_profile, CFG.num_profiles-1);
// refresh favorites list
Switch_Favorites(CFG.profile_start_favorites[CFG.current_profile]);
int new_theme;
if (CFG.profile_theme[CFG.current_profile] == -1)
new_theme = CFG.profile_theme[0];
else new_theme = CFG.profile_theme[CFG.current_profile];
CFG_switch_theme(new_theme);
Video_DrawBg();
if (CFG.save_filter) {
switch (CFG.profile_filter[CFG.current_profile]) {
case FILTER_GENRE:
filter_games_set(CFG.profile_filter[CFG.current_profile], CFG.profile_filter_genre[CFG.current_profile]);
break;
case FILTER_FEATURES:
filter_games_set(CFG.profile_filter[CFG.current_profile], CFG.profile_filter_feature[CFG.current_profile]);
break;
case FILTER_CONTROLLER:
filter_games_set(CFG.profile_filter[CFG.current_profile], CFG.profile_filter_controller[CFG.current_profile]);
break;
case FILTER_GAME_TYPE:
filter_games_set(CFG.profile_filter[CFG.current_profile], CFG.profile_filter_gametype[CFG.current_profile]);
break;
case FILTER_SEARCH:
strncpy(search_str, CFG.profile_search_string[CFG.current_profile], 100);
filter_games_set(CFG.profile_filter[CFG.current_profile], CFG.profile_search_field[CFG.current_profile]);
break;
default:
filter_games_set(CFG.profile_filter[CFG.current_profile], 0);
break;
}
}
redraw_cover = 1;
Cache_Invalidate();
break;
case 2:
CFG_switch_theme(cur_theme + change);
CFG.profile_theme[CFG.current_profile] = cur_theme;
Video_DrawBg();
redraw_cover = 1;
Cache_Invalidate();
break;
case 3:
Menu_Device();
return 0;
case 4:
Menu_Partition(true);
return 0;
case 5:
CHANGE(CFG.wiird, 2);
break;
case 6:
CHANGE(CFG.default_gc_loader, 2);
break;
case 7:
//cache_wait_idle();
Download_All_Covers(change > 0);
Cache_Invalidate();
if (header) Gui_DrawCover(header->id);
Menu_PrintWait();
break;
case 8:
Download_XML();
Download_Titles();
break;
case 9:
Download_Plugins();
break;
case 10:
Theme_Update();
break;
case 11:
Online_Update();
break;
case 12:
Save_Debug();
Save_IOS_Hash();
sleep(2);
break;
case 13:
Menu_All_IOS_Info();
break;
}
}
// debug
//if (buttons & WPAD_BUTTON_MINUS) { rows--; goto calc_rows; }
//if (buttons & WPAD_BUTTON_PLUS) { rows++; goto calc_rows; }
// HOME button
if (buttons & CFG.button_exit.mask) {
Handle_Home();
}
if (buttons & CFG.button_save.mask) {
Menu_Save_Settings();
}
if (buttons & WPAD_BUTTON_PLUS) {
printf("\n");
mem_stat();
time_stats();
Menu_PrintWait();
}
/*
if (buttons & WPAD_BUTTON_MINUS) {
printf("\n");
printf("Reloading ");
Music_Pause();
CURR_IOS_IDX = -1;
ReloadIOS(1,1);
printf("\n");
Music_UnPause();
sleep(1);
}
*/
if (buttons & CFG.button_other.mask) return 1;
if (buttons & CFG.button_cancel.mask) break;
}
return 0;
}
void Menu_Options()
{
int ret = 1;
while(ret)
{
if (gameCnt) {
ret = Menu_Game_Options();
}
if (ret) {
ret = Menu_Global_Options();
}
}
}
bool go_gui = false;
extern int action_alpha;
void DoAction(int action)
{
if (action & CFG_BTN_REMAP) return;
switch(action) {
case CFG_BTN_NOTHING:
break;
case CFG_BTN_OPTIONS:
if (!CFG.disable_options) Menu_Options();
break;
case CFG_BTN_GUI:
if (go_gui) {
action_string[0] = 0;
action_alpha=0;
}
if (CFG.gui) go_gui = !go_gui;
break;
case CFG_BTN_REBOOT:
Con_Clear();
Restart();
break;
case CFG_BTN_EXIT:
Con_Clear();
printf("\n");
printf_("Exiting...");
__console_flush(0);
Sys_Exit();
break;
case CFG_BTN_SCREENSHOT:
__console_flush(0);
Make_ScreenShot();
break;
case CFG_BTN_INSTALL:
Menu_Install();
break;
case CFG_BTN_REMOVE:
Menu_Remove();
break;
case CFG_BTN_MAIN_MENU:
Menu_Views();
break;
case CFG_BTN_GLOBAL_OPS:
if (!CFG.disable_options) Menu_Global_Options();
break;
case CFG_BTN_PROFILE:
if (CFG.current_profile == CFG.num_profiles-1)
CFG.current_profile = 0;
else
CFG.current_profile++;
Switch_Favorites(CFG.profile_start_favorites[CFG.current_profile]);
int new_theme;
if (CFG.profile_theme[CFG.current_profile] == -1)
new_theme = CFG.profile_theme[0];
else new_theme = CFG.profile_theme[CFG.current_profile];
CFG_switch_theme(new_theme);
Video_DrawBg();
if (CFG.save_filter) {
switch (CFG.profile_filter[CFG.current_profile]) {
case FILTER_GENRE:
filter_games_set(CFG.profile_filter[CFG.current_profile], CFG.profile_filter_genre[CFG.current_profile]);
break;
case FILTER_FEATURES:
filter_games_set(CFG.profile_filter[CFG.current_profile], CFG.profile_filter_feature[CFG.current_profile]);
break;
case FILTER_CONTROLLER:
filter_games_set(CFG.profile_filter[CFG.current_profile], CFG.profile_filter_controller[CFG.current_profile]);
break;
case FILTER_GAME_TYPE:
filter_games_set(CFG.profile_filter[CFG.current_profile], CFG.profile_filter_gametype[CFG.current_profile]);
break;
case FILTER_SEARCH:
strncpy(search_str, CFG.profile_search_string[CFG.current_profile], 100);
filter_games_set(CFG.profile_filter[CFG.current_profile], CFG.profile_search_field[CFG.current_profile]);
break;
default:
filter_games_set(CFG.profile_filter[CFG.current_profile], 0);
break;
}
}
if (gameCnt) Gui_DrawCover(gameList[gameSelected].id);//redraw_cover = 1;
Cache_Invalidate();
sprintf(action_string, gt("Profile: %s"), CFG.profile_names[CFG.current_profile]);
if (go_gui) action_alpha = 0xFF;
break;
case CFG_BTN_FAVORITES:
{
extern void reset_sort_default();
reset_sort_default();
Switch_Favorites(!enable_favorite);
}
break;
case CFG_BTN_BOOT_GAME:
Menu_Boot(0);
break;
case CFG_BTN_BOOT_DISC:
Menu_Boot(1);
break;
case CFG_BTN_THEME:
CFG_switch_theme(cur_theme + 1);
CFG.profile_theme[CFG.current_profile] = cur_theme;
Video_DrawBg();
if (gameCnt) Gui_DrawCover(gameList[gameSelected].id);//redraw_cover = 1;
Cache_Invalidate();
sprintf(action_string, gt("Theme: %s"), theme_list[cur_theme]);
if (go_gui) action_alpha = 0xFF;
break;
case CFG_BTN_UNLOCK:
if (CFG.admin_lock) Menu_Unlock();
break;
case CFG_BTN_HBC:
Con_Clear();
printf("\n");
printf_("HBC...");
__console_flush(0);
Sys_HBC();
break;
case CFG_BTN_SORT:
if (sort_desc) {
sort_desc = 0;
if (sort_index == sortCnt - 1)
sort_index = 0;
else
sort_index = sort_index + 1;
sortList(sortTypes[sort_index].sortAsc);
} else {
sort_desc = 1;
sortList(sortTypes[sort_index].sortDsc);
}
if (gameCnt) Gui_DrawCover(gameList[gameSelected].id);//redraw_cover = 1;
sprintf(action_string, gt("Sort: %s-%s"), sortTypes[sort_index].name, (sort_desc) ? "DESC":"ASC");
break;
case CFG_BTN_FILTER:
Menu_Filter();
break;
case CFG_BTN_RANDOM:
gameSelected = (rand() >> 16) % gameCnt;
__Menu_ScrollStartList();
break;
default:
// Priiloader magic words, and channels
if ((action & 0xFF) < 'a') {
// upper case final letter implies channel
Con_Clear();
Sys_Channel(action);
} else {
// lower case final letter implies magic word
Con_Clear();
*(vu32*)0x8132FFFB = action;
Restart();
}
break;
}
}
int get_buttonmap_num(int buttons)
{
int i;
for (i = 4; i < MAX_BUTTONS; i++) {
if (buttons & buttonmap[MASTER][i]) return i;
}
return -1;
}
int get_buttonmap_action(int button_num)
{
int i = button_num;
if (i < 4 || i >= MAX_BUTTONS) return CFG_BTN_NOTHING;
return *(&CFG.button_M + (i - 4));
}
int get_button_action(int buttons)
{
int i = get_buttonmap_num(buttons);
return get_buttonmap_action(i);
}
void __Menu_Controls(void)
{
if (go_gui) goto gui_mode;
//u32 buttons = Wpad_WaitButtons();
u32 buttons = Wpad_WaitButtonsRpt();
/* UP/DOWN buttons */
if (buttons & WPAD_BUTTON_UP)
__Menu_MoveList(-1);
if (buttons & WPAD_BUTTON_DOWN)
__Menu_MoveList(1);
/* LEFT/RIGHT buttons */
if (buttons & WPAD_BUTTON_LEFT) {
//__Menu_MoveList(-ENTRIES_PER_PAGE);
if (CFG.cursor_jump) {
__Menu_MoveList(-CFG.cursor_jump);
} else {
__Menu_MoveList((gameSelected-gameStart == 0) ? -ENTRIES_PER_PAGE : -(gameSelected-gameStart));
}
}
if (buttons & WPAD_BUTTON_RIGHT) {
//__Menu_MoveList(ENTRIES_PER_PAGE);
if (CFG.cursor_jump) {
__Menu_MoveList(CFG.cursor_jump);
} else {
__Menu_MoveList((gameSelected-gameStart == (ENTRIES_PER_PAGE - 1)) ? ENTRIES_PER_PAGE : ENTRIES_PER_PAGE - (gameSelected-gameStart) - 1);
}
}
check_buttons:
if (CFG.admin_lock) {
if (buttons & CFG.button_other.mask) {
static long long t_start;
long long t_now;
unsigned ms_diff = 0;
bool display_unlock = false;
Con_Clear();
t_start = gettime();
while (!display_unlock && (Wpad_Held() & CFG.button_other.mask)) {
buttons = Wpad_GetButtons();
VIDEO_WaitVSync();
t_now = gettime();
ms_diff = diff_msec(t_start, t_now);
if (ms_diff > 5000)
display_unlock = true;
}
if (display_unlock)
Menu_Unlock();
else
buttons = buttonmap[MASTER][CFG.button_other.num];
}
}
/* A button */
//if (buttons & CFG.button_confirm.mask)
// Menu_Boot(0);
int i;
for (i = 4; i < MAX_BUTTONS; i++) {
if (buttons & buttonmap[MASTER][i])
DoAction(*(&CFG.button_M + (i - 4)));
}
if (go_gui) {
gui_mode:;
int prev_sel = gameSelected;
CFG.gui = 1; // disable auto start
buttons = Gui_Mode();
if (prev_sel != gameSelected) {
// List scrolling
__Menu_ScrollStartList();
}
// if only returning to con mode, clear button status
/*if (CFG.buttons == CFG_BTN_OPTIONS_1) {
if (buttons & CFG.button_cancel.mask) buttons = 0;
} else if (CFG.buttons == CFG_BTN_OPTIONS_B) {
if (buttons & CFG.button_other.mask) buttons = 0;
}
*/
// if action started from gui, process it then return to gui
//if (buttons) {
// go_gui = true;
goto check_buttons;
//}
}
}
const char *get_dev_name(int dev)
{
switch (dev) {
case WBFS_DEVICE_USB: return gt("USB Mass Storage Device");
case WBFS_DEVICE_SDHC: return gt("SD/SDHC Card");
}
return gt("Unknown");
}
void print_part(int i, PartList *plist)
{
partitionEntry *entry = &plist->pentry[i];
PartInfo *pinfo = &plist->pinfo[i];
f32 size = entry->size * (plist->sector_size / GB_SIZE);
if (plist->num == 1) {
printf(gt("RAW"));
} else {
printf("%s", i<4 ? "P" : "L");
printf("#%d", i+1);
}
printf(" %7.2f ", size);
if (pinfo->fs_index) {
bool is_ext = part_is_extended(entry->type);
if (is_ext) {
printf("%s", part_type_name(entry->type));
printf("/");
}
char pname[16];
sprintf(pname, "%s%d", get_fs_name(pinfo->fs_type), pinfo->fs_index);
printf("%-5s", pname);
if (pinfo->fs_type == PART_FS_WBFS) {
if (!is_ext) printf(" ");
if (WBFS_Mounted() && wbfs_part_idx == pinfo->fs_index) {
printf(gt("[USED]"));
}
} else {
MountPoint *m = mount_find_part(wbfsDev, entry->sector);
char mname[16] = "";
if (m) {
mount_name2drive(m->name, mname);
}
printf(" %-5s", mname);
if (WBFS_Selected() && entry->sector == wbfs_part_lba) {
printf(" ");
printf(gt("[USED]"));
}
}
} else {
printf("%s", part_type_name(entry->type));
char *pdata = part_type_data(entry->type);
if (pdata) {
if (strncmp(pdata,"FAT",3)==0
|| strncmp(pdata,"NTFS",4)==0
|| strncmp(pdata,"LINUX",5)==0)
{
printf("/?");
}
}
}
}
void Menu_Format_Partition(partitionEntry *entry, bool delete_fs)
{
int ret;
if (CFG.disable_format) return;
printf_x(gt("%s, please wait..."), delete_fs?gt("Deleting"):gt("Formatting"));
__console_flush(0);
// unmount if overwriting mounted fs
MountPoint *mp;
mp = mount_find_part(wbfsDev, entry->sector);
if (mp) {
Music_Pause();
UnmountFS(mp->name);
}
// WBFS_Close will unmount fat/wbfs too
WBFS_Close();
all_gameCnt = gameCnt = 0;
/* Format partition */
bool ok;
if (delete_fs) {
char buf[512];
memset(buf,0,sizeof(buf));
ret = -2;
ok = Device_WriteSectors(wbfsDev, entry->sector, 1, buf);
if (ok) ret = 0;
} else {
ret = WBFS_Format(entry->sector, entry->size);
ok = (ret == 0);
}
if (!ok) {
printf("\n");
printf_(gt("ERROR! (ret = %d)"), ret);
printf("\n");
} else {
printf(" ");
printf(gt("OK!"));
printf("\n");
}
if (mp) {
Music_UnPause();
}
printf("\n");
Menu_PrintWait();
}
void Menu_Sync_FAT()
{
extern MountTable mtab;
MountPoint *m;
struct statvfs vfs;
struct statvfs vfs2;
float free;
int ret;
int i;
char drive[32];
printf("\n");
printf("\n");
printf_x(gt("Sync FAT free space info?"));
printf("\n");
printf_(gt("(This can take a couple of minutes)"));
printf("\n");
printf("\n");
for (i=0; i<mtab.num; i++)
{
m = &mtab.point[i];
if (m->fstype == PART_FS_FAT) {
free = 0;
mount_name2drive(m->name, drive);
memset(&vfs, 0, sizeof(vfs));
ret = statvfs(drive, &vfs);
if (ret == 0) {
free = (f32)vfs.f_frsize * (f32)vfs.f_bfree / GB_SIZE;
} else {
printf(gt("ERROR!"));
printf("\n");
}
printf_("[%s] %s %.3f %s %s\n",
m->device == WBFS_DEVICE_USB ? "USB" : "SD",
drive, free, gt("GB"), gt("free"));
}
}
printf("\n");
printf_h(gt("Press %s button to continue."), (button_names[CFG.button_confirm.num]));
printf("\n");
printf_h(gt("Press %s button to go back."), (button_names[CFG.button_cancel.num]));
printf("\n");
int button;
while(1) {
button = Wpad_WaitButtonsCommon();
if (button & CFG.button_cancel.mask) return;
if (button & CFG.button_confirm.mask) break;
}
printf("\n");
printf_(gt("Synchronizing, please wait."));
printf("\n");
printf("\n");
for (i=0; i<mtab.num; i++)
{
m = &mtab.point[i];
if (m->fstype == PART_FS_FAT) {
free = 0;
mount_name2drive(m->name, drive);
memset(&vfs, 0, sizeof(vfs));
ret = statvfs(drive, &vfs);
if (ret == 0) {
free = (f32)vfs.f_frsize * (f32)vfs.f_bfree / GB_SIZE;
}
printf_("[%s] %s %.3f %s %s\n",
m->device == WBFS_DEVICE_USB ? "USB" : "SD",
drive, free, gt("GB"), gt("free"));
printf_("...");
memset(&vfs2, 0, sizeof(vfs2));
memcpy(&vfs2.f_flag, "SCAN", 4);
ret = statvfs(drive, &vfs2);
if (ret == 0) {
if (vfs.f_bfree == vfs2.f_bfree) {
printf(gt("OK!"));
printf("\n");
} else {
free = (f32)vfs2.f_frsize * (f32)vfs2.f_bfree / GB_SIZE;
printf("%.3f %s %s\n", free, gt("GB"), gt("free"));
}
} else {
printf(gt("ERROR!"));
printf("\n");
}
printf("\n");
}
}
Menu_PrintWait();
}
void Menu_Partition(bool must_select)
{
if (!MountWBFS) {
printf_x(gt("ERROR: WBFS not mounted!"));
printf("\n");
sleep(4);
return;
}
PartList plist;
partitionEntry *entry;
PartInfo *pinfo;
int i;
s32 ret = 0;
int is_raw = 0;
rescan:
/* Get partition entries */
ret = Partition_GetList(wbfsDev, &plist);
if (ret < 0) {
printf_x(gt("ERROR: No partitions found! (ret = %d)"), ret);
printf("\n");
sleep(4);
return;
}
if (plist.num == 1) is_raw = 1; else is_raw = 0;
struct Menu menu;
char active[MAX_PARTITIONS_EX];
menu_init(&menu, plist.num);
menu_init_active(&menu, active, MAX_PARTITIONS_EX);
loop:
menu_begin(&menu);
/* Clear console */
Con_Clear();
FgColor(CFG.color_header);
printf_x(gt("Select a partition"));
printf(":\n\n");
DefaultColor();
printf_("[ %s ]\n\n", get_dev_name(wbfsDev));
printf_("P# %s\n", gt("Size(GB) Type Mount Used"));
printf_("-----------------------------\n");
// P#1 400.00 FAT1 usb:
// P#2 400.00 FAT2 game: [USED]
// P#3 400.00 WBFS1
// P#4 400.00 NTFS
int invalid_ext = -1;
for (i = 0; i < plist.num; i++) {
partitionEntry *entry = &plist.pentry[i];
active[i] = part_valid_data(entry);
MENU_MARK();
print_part(i, &plist);
printf("\n");
if (part_is_extended(entry->type)
&& plist.pinfo[i].fs_type == PART_FS_WBFS
&& plist.pinfo[i].fs_index > 0
&& i < 4)
{
invalid_ext = i;
}
}
printf("\n");
printf_h(gt("Press %s button to select."), (button_names[CFG.button_confirm.num]));
printf("\n");
printf_h(gt("Press %s button to go back."), (button_names[CFG.button_cancel.num]));
printf("\n");
printf_h(gt("Press %s button to sync FAT."), (button_names[CFG.button_save.num]));
printf("\n");
if (!CFG.disable_format) {
if (is_raw) {
printf_("NOTE: RAW partition format\n"
"and delete not supported");
} else {
printf_h(gt("Press %s button to format WBFS."),
(button_names[NUM_BUTTON_PLUS]));
printf("\n");
printf_h(gt("Press %s button to delete FS."),
(button_names[NUM_BUTTON_MINUS]));
}
printf("\n");
if (invalid_ext >= 0) {
printf_("\n");
printf_(gt("NOTE: partition P#%d is type EXTEND but\n"
"contains a WBFS filesystem. This is an\n"
"invalid setup. Press %s to change the\n"
"partition type from EXTEND to data."),
invalid_ext+1, (button_names[CFG.button_other.num]));
printf("\n");
printf_h(gt("Press %s button to fix EXTEND/WBFS."),
(button_names[CFG.button_other.num]));
printf("\n");
}
}
u32 buttons = Wpad_WaitButtonsCommon();
menu_move(&menu, buttons);
// B button
if (buttons & CFG.button_cancel.mask) {
if (must_select) {
if (WBFS_Selected()) return;
printf("\n");
printf_(gt("No partition selected!"));
printf("\n");
sleep(2);
} else {
return;
}
}
// A button
if (buttons & CFG.button_confirm.mask) {
i = menu.current;
entry = &plist.pentry[i];
pinfo = &plist.pinfo[i];
if (pinfo->fs_index) {
char pname[16];
sprintf(pname, "%s%d", get_fs_name(pinfo->fs_type), pinfo->fs_index);
printf(gt("Opening partition: %s"), pname);
printf("\n");
__console_flush(0);
ret = WBFS_OpenPart(pinfo->fs_type, pinfo->fs_index, entry->sector,
entry->size, CFG.partition);
if (ret == 0 && WBFS_Selected()) {
if (must_select) {
// called from global options
__Menu_GetEntries();
}
return;
}
}
}
// +/- button
if ((buttons & WPAD_BUTTON_MINUS || buttons & WPAD_BUTTON_PLUS)
&& !CFG.disable_format && !is_raw)
{
i = menu.current;
entry = &plist.pentry[i];
if (part_valid_data(entry)) {
const char *act = gt("format");
if (buttons & WPAD_BUTTON_MINUS) act = gt("delete");
printf("\n");
printf_x(gt("Are you sure you want to %s\n"
"this partition?"), act);
printf("\n\n");
printf_("");
print_part(i, &plist);
printf("\n\n");
if (Menu_Confirm(0)) {
printf("\n");
Menu_Format_Partition(entry, buttons & WPAD_BUTTON_MINUS);
goto rescan;
}
}
}
// 2 button
if (buttons & CFG.button_save.mask)
{
Menu_Sync_FAT();
}
// 1 button
if (buttons & CFG.button_other.mask
&& !CFG.disable_format
&& invalid_ext >= 0)
{
printf("\n");
printf_x(gt("Are you sure you want to FIX\n"
"this partition?"));
printf("\n\n");
printf_("");
print_part(invalid_ext, &plist);
printf("\n\n");
if (Menu_Confirm(0)) {
printf("\n");
printf_(gt("Fixing EXTEND partition..."));
ret = Partition_FixEXT(wbfsDev, invalid_ext, plist.sector_size);
printf("%s\n", ret ? gt("FAIL") : gt("OK"));
printf_(gt("Press any button..."));
Wpad_WaitButtonsCommon();
goto rescan;
}
}
goto loop;
}
void Menu_Device(void)
{
static int first_time = 1;
int save_wbfsDev = wbfsDev;
s32 ret;
if (CFG.device == CFG_DEV_USB) {
wbfsDev = WBFS_DEVICE_USB;
goto mount_dev;
}
if (CFG.device == CFG_DEV_SDHC) {
wbfsDev = WBFS_DEVICE_SDHC;
goto mount_dev;
}
restart:
/* Ask user for device */
for (;;) {
//Enable the console
Gui_Console_Enable();
/* Clear console */
Con_Clear();
FgColor(CFG.color_header);
printf_x(gt("Select WBFS device:"));
printf("\n");
DefaultColor();
printf_("< %s >\n\n", get_dev_name(wbfsDev));
FgColor(CFG.color_help);
printf_(gt("Press %s/%s to select device."), (button_names[NUM_BUTTON_LEFT]), (button_names[NUM_BUTTON_RIGHT]));
printf("\n");
printf_(gt("Press %s button to continue."), (button_names[CFG.button_confirm.num]));
printf("\n");
if (!first_time) {
printf_(gt("Press %s button to cancel."), (button_names[CFG.button_cancel.num]));
printf("\n");
}
printf("\n");
Print_SYS_Info();
u32 buttons = Wpad_WaitButtonsCommon();
/* LEFT/RIGHT buttons */
if (buttons & WPAD_BUTTON_LEFT) {
if ((--wbfsDev) < WBFS_MIN_DEVICE)
wbfsDev = WBFS_MAX_DEVICE;
}
if (buttons & WPAD_BUTTON_RIGHT) {
if ((++wbfsDev) > WBFS_MAX_DEVICE)
wbfsDev = WBFS_MIN_DEVICE;
}
/* A button */
if (buttons & CFG.button_confirm.mask)
break;
// B button
if (buttons & CFG.button_cancel.mask) {
if (!first_time && WBFS_Selected()) {
wbfsDev = save_wbfsDev;
return;
}
}
}
mount_dev:;
CFG.device = CFG_DEV_ASK; // next time ask
ret = Retry_Init_Dev(wbfsDev, 1);
if (ret == WPAD_BUTTON_A) goto restart;
// If 1 is pressed that means to skip the WBFS-mounting and use GC only
MountWBFS = (ret != WPAD_BUTTON_1);
//usleep(100000); // 100ms
if (MountWBFS) {
// Mount usb fat partition if not already
// This is after device init, because of the timeout handling
MountUSB();
all_gameCnt = gameCnt = 0;
/* Try to open device */
//WBFS_Open();
if (strcasecmp(CFG.partition, "ask") == 0) {
Gui_Console_Enable();
goto jump_to_selection;
}
WBFS_OpenNamed(CFG.partition);
while (!WBFS_Selected()) {
//Enable the console
Gui_Console_Enable();
/* Clear console */
Con_Clear();
printf_x(gt("WARNING:"));
printf("\n\n");
printf_(gt("Partition: %s not found!"), CFG.partition);
printf("\n");
printf_(gt("Select a different partition"));
printf("\n");
if (!CFG.disable_format) {
printf_(gt("or format a WBFS partition."));
printf("\n");
}
printf("\n");
printf_h(gt("Press %s button to select a partition."), (button_names[CFG.button_confirm.num]));
printf("\n");
printf_h(gt("Press %s button to change device."), (button_names[CFG.button_cancel.num]));
printf("\n");
printf_h(gt("Press %s button to exit."), (button_names[CFG.button_exit.num]));
printf("\n\n");
// clear button states
WPAD_Flush(WPAD_CHAN_ALL);
/* Wait for user answer */
for (;;) {
u32 buttons = Wpad_WaitButtonsCommon();
if (buttons & CFG.button_confirm.mask) break;
if (buttons & CFG.button_cancel.mask) goto restart;
}
// Select or Format partition
jump_to_selection:
Menu_Partition(false);
}
}
/* Get game list */
get_time(&TIME.gamelist1);
__Menu_GetEntries();
get_time(&TIME.gamelist2);
if (CFG.debug && first_time) {
Menu_PrintWait();
}
first_time = 0;
}
int Retry_Init_Dev(int device, int a_select)
{
static int ios_reloads = 0;
int max_ios_reloads = 1;
int timeout = 90;
int retry_menu = 4;
int ios_idx = CFG.game.ios_idx;
int ret;
int i;
for (i=0; i<timeout; i++) {
if (i == 1) {
//Enable the console
Gui_Console_Enable();
}
repaint:
if (a_select || i > 0) {
Con_Clear();
//MountPrint();
FgColor(CFG.color_header);
if (!a_select) {
printf("Searching for config.txt\n");
}
printf("\n");
printf_("[ %s ]\n", get_dev_name(device));
DefaultColor();
printf("\n");
printf_x(gt("Mounting device, please wait..."));
printf("\n");
printf_(gt("(%d seconds timeout)"), timeout);
printf("\n\n");
if (i >= retry_menu) {
printf_("%s\n", gt("Device is not responding!"));
if (device == WBFS_DEVICE_USB) {
printf_("%s\n", gt("Make sure USB port 0 is used!\n"
"(The one nearest to the edge)"));
}
printf_("%s\n", gt("You can also try unplugging\n"
"and plugging back the device,\n"
"or just wait some more"));
if (a_select) {
printf_h("%s\n", gt("Press A to select device"));
} else {
printf("\n\n");
printf_h("%s\n", gt("Press A to continue without config.txt"));
}
printf_h("%s\n", gt("Press B to exit to HBC"));
printf_h("%s\n", gt("Press HOME to reset"));
// Press 1 to skip WBFS mounting and switch to GC-only mode
printf_h("%s\n", gt("Press 1 to skip WBFS mounting"));
//if (first_time && ios_reloads < max_ios_reloads) {
if (ios_reloads < max_ios_reloads) {
printf_h(gt("Press 2 to reload IOS"));
printf(" < %s >\n", ios_str(ios_idx));
printf_h("%s\n", gt("Press LEFT/RIGHT to select IOS"));
} else {
Print_IOS_Info();
printf("\n");
}
}
}
if (i > 0) {
printf("\n");
printf_("Retry: %d ...\n", i);
}
fflush(stdout);
__console_flush(0);
/* Initialize WBFS */
ret = WBFS_Init_Dev(device);
if (ret >= 0) {
printf("\n");
printf_(gt("OK!"));
return 0;
}
if (i < retry_menu) {
sleep(1);
} else {
if (i == retry_menu) {
Wpad_Init();
}
int button = Wpad_WaitButtonsTimeout(1000);
switch (button) {
case WPAD_BUTTON_A:
return WPAD_BUTTON_A;
case WPAD_BUTTON_B:
Sys_HBC(0);
Sys_Exit();
break;
case WPAD_BUTTON_HOME:
Restart();
break;
case WPAD_BUTTON_1:
return WPAD_BUTTON_1;
case WPAD_BUTTON_2:
if (ios_reloads >= max_ios_reloads) {
goto repaint;
}
printf("\n");
Music_Pause();
cfg_ios_set_idx(ios_idx);
CURR_IOS_IDX = -1;
ReloadIOS(1,1);
printf("\n");
Music_UnPause();
ios_reloads++;
sleep(2);
i = retry_menu; // reset retry count
break;
case WPAD_BUTTON_LEFT:
ios_idx--;
if (ios_idx < 0) ios_idx = 0;
goto repaint;
case WPAD_BUTTON_RIGHT:
ios_idx++;
if (ios_idx > CFG_IOS_MAX) ios_idx = CFG_IOS_MAX;
goto repaint;
}
}
}
printf("\n");
printf_(gt("ERROR! (ret = %d)"), ret);
/* Restart wait */
Restart_Wait();
return -1;
}
u8 BCA_Data[64] ATTRIBUTE_ALIGN(32);
void Menu_DumpBCA(u8 *id)
{
int ret;
char fname[100];
memset(BCA_Data, 0, 64);
printf_("\n");
printf_(gt("Reading BCA..."));
printf("\n\n");
ret = WDVD_Read_Disc_BCA(BCA_Data);
hex_dump3(BCA_Data, 64);
printf_("\n");
if (ret) {
printf_(gt("ERROR reading BCA!"));
printf("\n\n");
goto out;
}
// save
snprintf(D_S(fname), "%s/%.6s.bca", USBLOADER_PATH, (char*)id);
if (!Menu_Confirm(gt("save"))) return;
printf("\n");
printf_(gt("Writing: %s"), fname);
printf("\n\n");
FILE *f = fopen(fname, "wb");
if (!f) {
printf_(gt("ERROR writing BCA!"));
printf("\n\n");
goto out;
}
fwrite(BCA_Data, 64, 1, f);
fclose(f);
out:
Menu_PrintWait();
}
void print_ntfs_write_error()
{
printf_x(gt("ERROR: NTFS write disabled!\n(set ntfs_write=1)"));
printf("\n");
}
void Menu_Install(void)
{
static struct discHdr header ATTRIBUTE_ALIGN(32);
s32 ret;
int i;
#ifdef FAKE_GAME_LIST
fake_games++;
__Menu_GetEntries();
return;
#endif
if (CFG.disable_install) return;
/* Clear console */
Con_Clear();
FgColor(CFG.color_header);
printf_x(gt("Install game"));
printf("\n\n");
DefaultColor();
char *nocover = "ZZZZZZ";
Gui_DrawCover((u8 *)nocover);
/* Disable WBFS mode */
Disc_SetWBFS(0, NULL);
// Wait for disc
//printf_x("Checking game disc...");
u32 cover = 0;
ret = WDVD_GetCoverStatus(&cover);
if (ret < 0) {
err1:
printf("\n");
printf_(gt("ERROR! (ret = %d)"), ret);
printf("\n");
goto out;
}
if (!(cover & 0x2)) {
printf_(gt("Please insert a game disc..."));
printf("\n\n");
printf_h(gt("Press %s to return"), (button_names[CFG.button_cancel.num]));
printf("\n\n");
do {
ret = WDVD_GetCoverStatus(&cover);
if (ret < 0) goto err1;
u32 buttons = Wpad_GetButtons();
if (buttons & CFG.button_cancel.mask) {
header = gameList[gameSelected];
Gui_DrawCover(header.id);
return;
}
VIDEO_WaitVSync();
} while(!(cover & 0x2));
}
// Open disc
printf_x(gt("Opening DVD disc..."));
ret = Disc_Open();
if (ret < 0) {
printf("\n");
printf_(gt("ERROR! (ret = %d)"), ret);
printf("\n");
goto out;
} else {
printf(" ");
printf(gt("OK!"));
printf("\n");
}
if (Disc_IsGC() == 0) {
Disc_ReadGCHeader(&header);
u64 real_size = 0;
Gui_DrawCover(header.id);
if (getHeaderById(gameList, gameCnt, header.id, header.disc)) {
FgColor(CFG.color_footer);
__Menu_PrintInfo2(&header, GC_GAME_SIZE, real_size);
DefaultColor();
printf_x(gt("ERROR: Game already installed!!"));
printf("\n\n");
goto out;
}
// TODO Speicherplatz auch auf USB pr<70>fen
int installDevice = WBFS_MIN_DEVICE;
/* Ask user for device */
for (;;) {
//Enable the console
//Gui_Console_Enable();
/* Clear console */
Con_Clear();
FgColor(CFG.color_header);
printf_x(gt("Select device:"));
printf("\n");
DefaultColor();
printf_("< %s >\n\n", get_dev_name(installDevice));
FgColor(CFG.color_help);
printf_(gt("Press %s/%s to select device."), (button_names[NUM_BUTTON_LEFT]), (button_names[NUM_BUTTON_RIGHT]));
printf("\n");
printf_(gt("Press %s button to continue."), (button_names[CFG.button_confirm.num]));
printf("\n");
printf_(gt("Press %s button to cancel."), (button_names[CFG.button_cancel.num]));
printf("\n");
printf("\n");
//Print_SYS_Info();
u32 buttons = Wpad_WaitButtonsCommon();
/* LEFT/RIGHT buttons */
if (buttons & WPAD_BUTTON_LEFT) {
if ((--installDevice) < WBFS_MIN_DEVICE)
installDevice = WBFS_MAX_DEVICE;
}
if (buttons & WPAD_BUTTON_RIGHT) {
if ((++installDevice) > WBFS_MAX_DEVICE)
installDevice = WBFS_MIN_DEVICE;
}
/* A button */
if (buttons & CFG.button_confirm.mask)
break;
// B button
if (buttons & CFG.button_cancel.mask) {
goto out2;
}
}
f32 free, used, total;
if (installDevice == WBFS_DEVICE_USB) {
WBFS_DiskSpace(&used, &free);
} else if (installDevice == WBFS_DEVICE_SDHC) {
SD_DiskSpace(&used, &free);
}
total = used + free;
printf_x(installDevice == WBFS_DEVICE_USB ? "usb: " : "sd: ");
printf(gt("%.1fGB free of %.1fGB"), free, total);
printf("\n\n");
bench_io();
// require +128kb for operating safety
if ((f32)GC_GAME_SIZE + (f32)128*1024 >= free * GB_SIZE) {
printf_x(gt("ERROR: not enough free space!!"));
printf("\n\n");
goto out;
}
printf("\n");
FgColor(CFG.color_footer);
__Menu_PrintInfo2(&header, GC_GAME_SIZE, real_size);
DefaultColor();
printf("\n");
printf("\n");
printf_h(gt("Press %s button to continue."), (button_names[CFG.button_confirm.num]));
printf("\n");
printf_h(gt("Press %s button to go back."), (button_names[CFG.button_cancel.num]));
printf("\n");
DefaultColor();
for (;;) {
u32 buttons = Wpad_WaitButtonsCommon();
if (buttons & CFG.button_confirm.mask) break;
if (buttons & CFG.button_cancel.mask) {
goto out2;
}
}
printf_x(gt("Installing game, please wait..."));
printf("\n\n");
ret = Disc_DumpGCGame(installDevice == WBFS_DEVICE_SDHC);
goto out_did_install; //join common code after tried installing
}
// GC installing is ok, Wii won't work because no HDD is mounted
if (!MountWBFS) {
printf_x(gt("ERROR: WBFS not mounted!"));
printf("\n");
printf_x(gt("Can't install Wii games!"));
printf("\n");
goto out;
}
/* Check disc */
ret = Disc_IsWii();
if (ret < 0) {
printf_x(gt("ERROR: Not a Wii disc!!"));
printf("\n");
goto out;
}
/* Read header */
Disc_ReadHeader(&header);
u64 comp_size = 0, real_size = 0;
WBFS_DVD_Size(&comp_size, &real_size);
Gui_DrawCover(header.id);
printf("\n");
__Menu_PrintInfo2(&header, comp_size, real_size);
// force fat freespace update when installing
extern int wbfs_fat_vfs_have;
wbfs_fat_vfs_have = 0;
// Disk free space
f32 free, used, total;
WBFS_DiskSpace(&used, &free);
total = used + free;
//printf_x("WBFS: %.1fGB free of %.1fGB\n\n", free, total);
printf_x("%s: ", CFG.partition);
printf(gt("%.1fGB free of %.1fGB"), free, total);
printf("\n\n");
bench_io();
if (check_dual_layer(real_size, NULL)) print_dual_layer_note();
/* Check if game is already installed */
int way_out = 0;
ret = WBFS_CheckGame(header.id);
if (ret) {
printf_x(gt("ERROR: Game already installed!!"));
printf("\n\n");
//goto out;
way_out = 1;
}
// require +128kb for operating safety
if ((f32)comp_size + (f32)128*1024 >= free * GB_SIZE) {
printf_x(gt("ERROR: not enough free space!!"));
printf("\n\n");
//goto out;
way_out = 1;
}
// check ntfs write
if (wbfs_part_fs == PART_FS_NTFS && CFG.ntfs_write == 0) {
print_ntfs_write_error();
printf("\n");
//goto out;
way_out = 1;
}
// get confirmation
retry:
if (!way_out) {
printf_h(gt("Press %s button to continue."), (button_names[CFG.button_confirm.num]));
printf("\n");
}
printf_h(gt("Press %s button to dump BCA."), (button_names[CFG.button_other.num]));
printf("\n");
printf_h(gt("Press %s button to go back."), (button_names[CFG.button_cancel.num]));
printf("\n");
DefaultColor();
for (;;) {
u32 buttons = Wpad_WaitButtonsCommon();
if (!way_out)
if (buttons & CFG.button_confirm.mask) break;
if (buttons & CFG.button_cancel.mask) {
goto out2;
}
if (buttons & CFG.button_other.mask) {
Menu_DumpBCA(header.id);
if (way_out) {
goto out2;
}
Con_Clear();
printf_x(gt("Install game"));
printf("\n\n");
__Menu_PrintInfo2(&header, comp_size, real_size);
printf_x(gt("WBFS: %.1fGB free of %.1fGB"), free, total);
printf("\n\n");
goto retry;
}
}
printf_x(gt("Installing game, please wait..."));
printf("\n\n");
// Pause the music
Music_Pause();
/* Install game */
ret = WBFS_AddGame();
out_did_install:
// UnPause the music
Music_UnPause();
if (ret < 0) {
printf_x(gt("Installation ERROR! (ret = %d)"), ret);
printf("\n");
goto out;
}
/* Remember the game id because reload will free the memory its currently using */
u8 new_game_id[6];
memcpy(new_game_id, header.id, 6);
/* Reload entries */
__Menu_GetEntries();
/* Set just installed game as selected */
for (i=0; i<gameCnt; i++) {
if (strncmp((char*)gameList[i].id, (char*)new_game_id, 6) == 0) {
gameSelected = i;
break;
}
}
/* Turn on the Slot Light */
wiilight(1);
bool coversdone = false;
out:
// clear buttons status
WPAD_Flush(WPAD_CHAN_ALL);
if (!coversdone) {
printf("\n");
printf_h(gt("Press button %s to download covers."), (button_names[CFG.button_other.num]));
}
printf("\n");
printf_h(gt("Press button %s to eject DVD."), (button_names[CFG.button_confirm.num]));
printf("\n");
printf_h(gt("Press any button to continue..."));
printf("\n\n");
/* Wait for any button */
u32 buttons = Wpad_WaitButtons();
/* Turn off the Slot Light */
wiilight(0);
// button A
if (buttons & CFG.button_confirm.mask) {
printf_(gt("Ejecting DVD..."));
printf("\n");
WDVD_Eject();
sleep(1);
} else if (!coversdone && (buttons & CFG.button_other.mask)) {
Download_Cover((char *)new_game_id, true, true);
coversdone = true;
Gui_DrawCover(header.id);
goto out;
}
out2:
printf("\n");
printf_(gt("Stopping DVD..."));
WDVD_StopMotor();
printf(gt("OK"));
Gui_DrawCover(gameList[gameSelected].id);
}
void Menu_Remove(void)
{
struct discHdr *header = NULL;
s32 ret;
#ifdef FAKE_GAME_LIST
fake_games--;
__Menu_GetEntries();
return;
#endif
if (CFG.disable_remove) return;
/* No game list */
if (!gameCnt)
return;
/* Selected game */
header = &gameList[gameSelected];
/* Clear console */
Con_Clear();
FgColor(CFG.color_header);
printf_x(gt("Are you sure you want to remove this\n"
"game?"));
printf("\n\n");
DefaultColor();
/* Show game info */
__Menu_PrintInfo(header);
if (!Menu_Confirm(0)) return;
printf("\n\n");
printf_x(gt("Removing game, please wait..."));
fflush(stdout);
/* Remove game */
if (header->magic == GC_GAME_ON_DRIVE) {
ret = DML_RemoveGame(*header);
} else if (header->magic == CHANNEL_MAGIC) {
ret = Channel_RemoveGame(header);
} else {
ret = WBFS_RemoveGame(header->id);
if (ret < 0) {
printf("\n");
printf_(gt("ERROR! (ret = %d)"), ret);
printf("\n");
goto out;
} else {
printf(" ");
printf(gt("OK!"));
}
}
/* Reload entries */
__Menu_GetEntries();
out:
printf("\n");
Menu_PrintWait();
}
extern s32 __WBFS_ReadDVD(void *fp, u32 lba, u32 len, void *iobuf);
extern int block_used(u8 *used,u32 i,u32 wblk_sz);
void bench_io()
{
if (CFG.debug != 8) return;
//int buf[64*1024/4];
//int buf[32*1024/4];
int size_buf = 128*1024;
int size_total = 64*1024*1024;
//int size_total = 256*1024;
void *buf = allocate_memory(size_buf);
int i, ret;
int ms = 0;
int count = 0;
int count_max = size_total / size_buf;
int size;
wiidisc_t *d = 0;
u8 *used = 0;
int n_wii_sec_per_disc = 143432*2;//support for double layers discs..
int wii_sec_sz = 0x8000;
int n_wii_sec_per_buf = size_buf / wii_sec_sz;
//printf("\nread: %d\n", n_wii_sec_per_buf); __console_flush(0);
printf("\n");
printf(gt("Running benchmark, please wait"));
printf("\n");
__console_flush(0);
used = calloc(n_wii_sec_per_disc,1);
d = wd_open_disc(__WBFS_ReadDVD, NULL);
if (!d) { printf(gt("unable to open wii disc")); goto out; }
wd_build_disc_usage(d,ALL_PARTITIONS,used);
wd_close_disc(d);
d = 0;
printf(gt("linear...")); printf("\n"); __console_flush(0);
dbg_time1();
for (i=0; i<n_wii_sec_per_disc / n_wii_sec_per_buf; i++) {
if (block_used(used,i,n_wii_sec_per_buf)) {
u32 offset = i * (size_buf>>2);
ret = __WBFS_ReadDVD(NULL,offset, size_buf,buf);
if (ret) {
printf(" %d = %d\n", i, ret); __console_flush(0);
Wpad_WaitButtonsCommon();
}
count++;
if (count >= count_max) break;
}
}
ms = dbg_time2("64mb read time (ms):");
size = count * size_buf;
printf("\n");
printf(gt("linear read speed: %.2f mb/s"),
(float)size / ms * 1000 / 1024 / 1024);
printf("\n");
count = 0;
printf(gt("random...")); printf("\n"); __console_flush(0);
dbg_time1();
for (i = n_wii_sec_per_disc / n_wii_sec_per_buf - 1; i>0; i--) {
if (block_used(used,i,n_wii_sec_per_buf)) {
u32 offset = i * (size_buf>>2);
ret = __WBFS_ReadDVD(NULL,offset, size_buf,buf);
if (ret) {
printf(" %d = %d\n", i, ret); __console_flush(0);
Wpad_WaitButtonsCommon();
}
count++;
if (count >= count_max) break;
}
}
ms = dbg_time2("64mb read time (ms):");
size = count * size_buf;
printf("\n");
printf(gt("random read speed: %.2f mb/s"),
(float)size / ms * 1000 / 1024 / 1024);
printf("\n");
out:
printf("\n");
Menu_PrintWait();
//exit(0);
}
void FmtGameInfoLong(u8 *id, int cols, char *game_desc, int size)
{
*game_desc = 0;
struct gameXMLinfo *g = get_game_info_id(id);
if (!g) return;
if (!LoadGameInfoFromXML(id)) return;
FmtGameInfo(game_desc, cols, size);
strappend(game_desc, "\n", size);
int i, n;
int req;
const char *s;
// required controllers
n = 0;
for (i=0; i<XML_NUM_ACCESSORY; i++) {
s = getControllerName(g, i, &req);
if (!s) break;
if (!req) continue;
if (n == 0) strappend(game_desc, "(", size);
if (n > 0) strappend(game_desc, ", ", size);
strappend(game_desc, s, size);
n++;
}
if (n) strappend(game_desc, ")", size);
// optional controllers
n = 0;
for (i=0; i<XML_NUM_ACCESSORY; i++) {
s = getControllerName(g, i, &req);
if (!s) break;
if (req) continue;
if (n == 0) strappend(game_desc, " [", size);
if (n > 0) strappend(game_desc, ", ", size);
strappend(game_desc, s, size);
n++;
}
if (n) strappend(game_desc, "]", size);
strappend(game_desc, "\n", size);
// genre
if (strcmp(g->genre,"") != 0) {
char tok[100], *p;
p = g->genre;
while (p) {
p = split_token(tok, p, ',', sizeof(tok));
for (i=0; i<genreCnt; i++) {
if (!strcmp(genreTypes[i][0],tok)) { //if found right genre
strappend(game_desc, genreTypes[i][1], size);
break;
}
if (i == genreCnt -1) //went through the whole list with no match
strappend(game_desc, tok, size); //add the origonal token
}
if (p)
strappend(game_desc, ", ", size); //add seperator
}
strappend(game_desc, "\n", size);
}
// synopsis
if (g->synopsis) {
strappend(game_desc, "\n", size);
strappend(game_desc, g->synopsis, size);
if (strlen(g->synopsis) == XML_MAX_SYNOPSIS - 1) {
// mark that the text was cut and there's more
strappend(game_desc, " ....", size);
}
}
extern char * unescape(char *input, int size);
unescape(game_desc, size);
char dots[4] = {0xE2, 0x80, 0xA6, 0};
str_replace_all(game_desc, dots, "...", size);
str_replace_all(game_desc, "\t", " ", size); // tabs
str_replace_all(game_desc, " ", " ", size); // multiple spaces
}
void Menu_Boot(bool disc)
{
/* Clear console */
if (!CFG.direct_launch) {
Con_Clear();
}
bool redraw_cover = false;
restart_menu_boot:;
static struct discHdr *disc_header = NULL;
struct discHdr *header;
bool gc = false;
if (disc) {
char *nocover = "ZZZZZZ";
Gui_DrawCover((u8 *)nocover);
if (!disc_header) {
disc_header = (struct discHdr *)allocate_memory(sizeof(struct discHdr));
}
memset(disc_header, 0, sizeof(struct discHdr));
header = disc_header;
} else {
header = &gameList[gameSelected];
}
s32 ret;
struct Game_CFG_2 *game_cfg = NULL;
u64 comp_size = 0, real_size = 0;
/* No game list */
if (!disc && !gameCnt)
return;
if (disc) {
Con_Clear();
/* Disable WBFS mode */
Disc_SetWBFS(0, NULL);
// Wait for disc
u32 cover = 0;
ret = WDVD_GetCoverStatus(&cover);
if (ret < 0) {
err1:
printf("\n");
printf_(gt("ERROR! (ret = %d)"), ret);
printf("\n");
goto out;
}
if (!(cover & 0x2)) {
printf_(gt("Please insert a game disc..."));
printf("\n\n");
printf_h(gt("Press %s to return"), (button_names[CFG.button_cancel.num]));
printf("\n\n");
do {
ret = WDVD_GetCoverStatus(&cover);
if (ret < 0) goto err1;
u32 buttons = Wpad_GetButtons();
if (buttons & CFG.button_cancel.mask) goto close;
VIDEO_WaitVSync();
} while(!(cover & 0x2));
}
// Open disc
printf_x(gt("Opening DVD disc..."));
ret = Disc_Open();
if (ret < 0) {
printf("\n");
printf_(gt("ERROR! (ret = %d)"), ret);
printf("\n");
goto out;
} else {
printf(" ");
printf("OK!");
printf("\n");
}
/* Check disc */
ret = Disc_IsWii();
if (ret < 0) {
ret = Disc_IsGC();
if (ret < 0) {
printf_x(gt("ERROR: Not a Wii disc!!"));
printf("\n");
goto out;
} else {
gc = true;
}
}
/* Read header */
Disc_ReadHeader(header);
Gui_DrawCover(header->id);
}
game_cfg = CFG_find_game(header->id);
// Get game size
if (!disc && (header->magic != GC_GAME_ON_DRIVE)&& (header->magic != CHANNEL_MAGIC))
WBFS_GameSize2(header->id, &comp_size, &real_size);
bool dl_warn = check_dual_layer(real_size, game_cfg);
bool can_skip = !CFG.confirm_start && !dl_warn && check_device(game_cfg, false);
bool do_skip = (disc && !CFG.confirm_start) || (!disc && can_skip);
SoundInfo snd;
u8 banner_title[84];
bool banner_playing = false;
bool banner_parsed = false;
memset(banner_title, 0, sizeof(banner_title));
memset(&snd, 0, sizeof(snd));
if (do_skip) {
printf("\n");
WBFS_Banner(header->id, &snd, banner_title, !do_skip,
CFG_read_active_game_setting(header->id).write_playlog);
/* Show game info */
__Menu_PrintInfo(header);
__console_flush(0);
goto skip_confirm;
}
char game_desc[XML_MAX_SYNOPSIS * 2] = "";
int page = 0;
int num_pages = 0;
int cols, rows;
CON_GetMetrics(&cols, &rows);
cols -= 1;
rows -= 11;
if (rows < 2) rows = 2;
FmtGameInfoLong(header->id, cols, game_desc, sizeof(game_desc));
con_wordwrap(game_desc, cols, sizeof(game_desc));
Gui_Console_Enable();
if (redraw_cover) {
__Menu_ShowCover();
redraw_cover = false;
}
L_repaint:
Con_Clear();
FgColor(CFG.color_header);
printf_x(gt("Start this game?"));
printf("\n");
DefaultColor();
/* Show game info */
if (disc) {
printf_("DISC:\n");
printf_("%s\n", get_title(header));
printf_("(%.6s)\n\n", header->id);
} else {
printf("\n");
__Menu_PrintInfo(header);
}
//__Menu_ShowGameInfo(false, header->id); /* load game info from XML */
//printf("\n\n");
FgColor(CFG.color_inactive);
print_page(game_desc, rows, page, &num_pages);
if (num_pages > 1) {
const char *page_str = gt("page");
int pad = cols - 8 - con_len(page_str);
printf("%*s %s: %d/%d", pad, "", page_str, page + 1, num_pages );
}
printf("\n");
DefaultColor();
//Does DL warning apply to launching discs too? Not sure
if (!disc && header->magic != GC_GAME_ON_DRIVE) {
check_device(game_cfg, true);
if (dl_warn) print_dual_layer_note();
}
// parse banner sound
if (!banner_parsed) {
__console_flush(0);
if (header->magic == GC_GAME_ON_DRIVE) {
parse_riff(&gc_wav, &snd);
} else if (header->magic == CHANNEL_MAGIC) {
CHANNEL_Banner(header, &snd);
} else {
WBFS_Banner(header->id, &snd, banner_title, !do_skip,
CFG_read_active_game_setting(header->id).write_playlog);
}
banner_parsed = true;
}
printf_h(gt("Press %s button to continue."), (button_names[CFG.button_confirm.num]));
printf("\n");
printf_h(gt("Press %s button to go back."), (button_names[CFG.button_cancel.num]));
printf("\n");
printf_h(gt("Press %s button for options."), (button_names[CFG.button_other.num]));
printf("\n");
//printf_h(gt("Press %s/%s to scroll description."),
// button_names[NUM_BUTTON_UP], button_names[NUM_BUTTON_DOWN]);
//printf("\n");
__console_flush(0);
// play banner sound
if (snd.dsp_data && !banner_playing) {
// pause mp3
Music_PauseVoice(true);
int fmt = (snd.channels == 2) ? VOICE_STEREO_16BIT : VOICE_MONO_16BIT;
SND_SetVoice(1, fmt, snd.rate, 0,
snd.dsp_data, snd.size,
255,255, //volume,volume,
NULL); //DataTransferCallback
banner_playing = true;
}
/* Wait for user answer */
u32 buttons;
for (;;) {
buttons = Wpad_WaitButtons();
if (buttons & CFG.button_confirm.mask) break;
if (buttons & CFG.button_cancel.mask) break;
if (buttons & CFG.button_other.mask) break;
if (buttons & CFG.button_exit.mask) break;
if (buttons & WPAD_BUTTON_UP) {
page--;
if (page < 0) page = 0;
goto L_repaint;
}
if (buttons & WPAD_BUTTON_DOWN) {
page++;
if (page >= num_pages) page = num_pages - 1;
if (page < 0) page = 0;
goto L_repaint;
}
if (!disc) {
if (buttons & WPAD_BUTTON_LEFT) break;
if (buttons & WPAD_BUTTON_RIGHT) break;
}
}
// stop banner sound, resume mp3
if (snd.dsp_data && banner_playing) {
SND_StopVoice(1);
SAFE_FREE(snd.dsp_data);
if (buttons & CFG.button_confirm.mask) {
// mute mp3
Music_Mute(true);
}
if ( (buttons & WPAD_BUTTON_LEFT) || (buttons & WPAD_BUTTON_RIGHT) ) {
// don't unpause mp3 when changing games
} else {
// unpause mp3
Music_PauseVoice(false);
}
banner_playing = false;
}
if (buttons & CFG.button_cancel.mask) goto close;
if (buttons & CFG.button_exit.mask) {
Handle_Home();
return;
}
if (buttons & CFG.button_other.mask) {
if (disc) Menu_Boot_Options(header, 1);
else Menu_Options();
return;
}
// LEFT/RIGHT: prev/next game
if (buttons & WPAD_BUTTON_LEFT) {
__Menu_MoveList(-1);
redraw_cover = true;
goto restart_menu_boot;
}
if (buttons & WPAD_BUTTON_RIGHT) {
__Menu_MoveList(+1);
redraw_cover = true;
goto restart_menu_boot;
}
// A button: continue to boot
printf("\n");
skip_confirm:
get_time(&TIME.run1);
if (game_cfg) {
CFG.game = game_cfg->curr;
if (CFG.game.ios_idx != CFG_IOS_AUTO) {
cfg_ios_set_idx(CFG.game.ios_idx);
} else {
tmd game_tmd;
memset(&game_tmd, 0, sizeof(game_tmd));
if (header->magic == CHANNEL_MAGIC) {
game_tmd.sys_version = getChannelReqIos(header); //store channel ios directly in sys_version for compatability
}
else if (header->magic == GC_GAME_ON_DRIVE) {
//do nothing
} else {
wbfs_disc_t* d = WBFS_OpenDisc(header->id);
if (d) {
wbfs_extract_tmd(d, &game_tmd);
WBFS_CloseDisc(d);
}
}
if (game_tmd.sys_version) {
set_recommended_cIOS_idx(TITLE_LOWER(game_tmd.sys_version), false);
}
}
//dbg_printf("set ios: %d idx: %d\n", CFG.ios, CFG.game.ios_idx);
/* Check for stub IOS */
ret = checkIOS(CFG.ios);
if (ret == -1) {
printf_x(gt("ERROR:"));
printf("\n");
printf_(gt("Custom IOS %d is a stub!\nPlease reinstall it."), CFG.ios);
printf("\n");
printf_(gt("Press any button..."));
printf("\n");
/* Wait for button */
Wpad_WaitButtonsCommon();
goto close;
} else if (ret == -2) {
printf_x(gt("ERROR:"));
printf("\n");
printf_(gt("Custom IOS %d could not be found!\nPlease install it."), CFG.ios);
printf("\n");
printf_(gt("Press any button..."));
printf("\n");
/* Wait for button */
Wpad_WaitButtonsCommon();
goto close;
}
}
if (!disc) {
if (CFG.game.alt_dol == ALT_DOL_DISC && *CFG.game.dol_name == 0) {
// show alt dol
Menu_Alt_Dol(header, &CFG.game, 0);
}
}
get_time(&TIME.playlog1);
if (header->magic == GC_GAME_ON_DRIVE || header->magic == CHANNEL_MAGIC) CFG.game.write_playlog = 0;
if (CFG.game.write_playlog && set_playrec(header->id, banner_title) < 0) {
printf_(gt("Error storing playlog file.\nStart from the Wii Menu to fix."));
printf("\n");
printf_h(gt("Press %s button to exit."), (button_names[CFG.button_exit.num]));
printf("\n");
if (!Menu_Confirm(0)) return;
}
get_time(&TIME.playlog2);
get_time(&TIME.gcard1);
if (gamercard_update((char *)(header->id))) {
printf_h(gt("Press %s button to exit."), (button_names[CFG.button_exit.num]));
printf("\n");
if (!Menu_Confirm(0)) return;
}
get_time(&TIME.gcard2);
printf("\n");
printf_x(gt("Booting game, please wait..."));
printf("\n\n");
if (header->magic == CHANNEL_MAGIC) {
get_time(&TIME.playstat1);
setPlayStat(header->id); //I'd rather do this after the check, but now you unmount fat before that ;)
get_time(&TIME.playstat2);
cfg_ios_set_idx(CFG.game.ios_idx);
char args[255][255] = {{"\0"}};
int i = 0;
sprintf(args[i++], "--ios=%d", CFG.ios);
if (strstr(CFG.nand_emu_path, "usb:")) {
strcpy(args[i++], "--auto=USB");
} else {
strcpy(args[i++], "--auto=SD");
}
sprintf(args[i++], "--path=%s", strchr(CFG.nand_emu_path, '/'));
sprintf(args[i++], "--gameIdLower=%02x%02x%02x%02x", header->id[0], header->id[1], header->id[2], header->id[3]);
sprintf(args[i++], "--videoMode=%d", CFG.game.video);
sprintf(args[i++], "--language=%d", CFG.game.language);
strcpy(args[i++], "--bootMethod=0");
if ((CFG.game.ocarina == 1) && (strncmp(USBLOADER_PATH,"usb",3) == 0))
CFG.game.ocarina = 2; // 1 is enabled on sd: , 2 is enabled on usb:
sprintf(args[i++], "--ocarina=%d", CFG.game.ocarina);
sprintf(args[i++], "--hooktype=%d", CFG.game.hooktype);
strcpy(args[i++], "--videoPatch=0");
strcpy(args[i++], "--debugger=0");
Menu_Plugin(CFG.game.channel_boot, args, i);
return;
}
//if we need to copy GC file
if (header->magic == GC_GAME_ON_DRIVE && !is_gc_game_on_bootable_drive(header)) {
char gamePath[255];
char drive[255];
sprintf(drive, "%s/", dm_boot_drive);
if (!fsop_DirExist(drive)) {
printf_x(gt("ERROR: the drive or partition %s/ is not connected!!"), dm_boot_drive);
printf("\n\n");
goto out;
}
sprintf(gamePath, "%s/games", dm_boot_drive);
if (!fsop_DirExist(gamePath))
{
fsop_MakeFolder(gamePath);
}
s32 del = delete_Old_Copied_DML_Game();
f32 free, used, total;
if (!strncmp("sd:", dm_boot_drive, 3))
SD_DiskSpace(&used, &free);
else
FAT_DiskSpace(&used, &free);
total = used + free;
printf_x("%s ", dm_boot_drive);
printf(gt("%.1fGB free of %.1fGB"), free, total);
printf("\n\n");
bench_io();
// require +128kb for operating safety
if ((f32)getDMLGameSize(header) + (f32)128*1024 >= free * GB_SIZE) {
printf_x(gt("ERROR: not enough free space!!"));
printf("\n\n");
if (del >= 0) __Menu_GetEntries();
goto out;
}
copy_DML_Game_to_SD(header);
}
if (gc || (header->magic == GC_GAME_ON_DRIVE && is_gc_game_on_bootable_drive(header)))
{
get_time(&TIME.playstat1);
setPlayStat(header->id); //I'd rather do this after the check, but now you unmount fat before that ;)
get_time(&TIME.playstat2);
memcpy((char *)0x80000000, header->id, 6);
dbg_printf("Setting language...\n");
if (CFG.game.language > 1 && CFG.game.language < 8)
GC_SetLanguage(CFG.game.language-1);
else
GC_SetLanguage(0);
char cheatPath[255];
char newCheatPath[255];
sprintf(newCheatPath, "%s/%.6s.gct", header->path, header->id);
if (CFG.game.ocarina) {
FILE *fp;
char *fat_drive = FAT_DRIVE;
int i;
printf_x(gt("Ocarina: Searching codes..."));
printf("\n");
for (i=0; i<3; i++) {
switch (i) {
case 0:
snprintf(cheatPath, sizeof(cheatPath), "%s/codes/%.6s.gct",
USBLOADER_PATH, header->id);
break;
case 1:
snprintf(cheatPath, sizeof(cheatPath), "%s/data/gecko/codes/%.6s.gct",
fat_drive, header->id);
break;
case 2:
snprintf(cheatPath, sizeof(cheatPath), "%s/codes/%.6s.gct",
fat_drive, header->id);
break;
}
printf(" %s\n", cheatPath);
fp = fopen(cheatPath, "rb");
if (fp) {
fclose(fp);
break;
}
if (i == 2 && strcmp(fat_drive, "usb:") == 0) {
// retry on sd:
fat_drive = "sd:";
i = 0;
}
}
}
if ((CFG.game.channel_boot == 3) || //Boot GC using Nintendont
(CFG.game.channel_boot == 0 && CFG.default_gc_loader == 2))
{
Nintendont_set_options(header, cheatPath, newCheatPath);
CFG.ios = 58;
CFG.game.ios_idx = 58;
// IOSPATCH_Apply();
char args[255][255] = {{"\0"}};
Menu_Plugin(PLUGIN_NINTENDONT, args, CFG.nin_cfg_mode ? 1 : 0 );
return; // should never get here
}
if ((CFG.game.channel_boot == 2) || //boot GC using Devolution
(CFG.game.channel_boot == 1 && CFG.dml == CFG_MIOS) ||
(CFG.game.channel_boot == 0 && CFG.default_gc_loader == 1))
{
if (getDMLDisk1Size(header) != GC_GAME_SIZE) {
printf_x(gt("Devolution only accepts clean dumps!\n"));
Wpad_WaitButtons();
goto out;
}
char devoPath[255];
char loaderPath[255];
snprintf(devoPath, sizeof(devoPath), "%s/game.iso", header->path);
DEVO_SetOptions(devoPath, CFG.game.mem_card_emu);
snprintf(D_S(loaderPath), "%s/loader.bin", USBLOADER_PATH);
sleep(1);
u8 *loader_bin = NULL;
FILE *fdev = fopen( loaderPath, "rb");
if (fdev)
{
fseek(fdev, 0, SEEK_END);
u32 size = ftell(fdev);
fseek(fdev, 0, SEEK_SET);
loader_bin = (u8*)mem2_alloc(size);
fread(loader_bin, 1, size, fdev);
fclose(fdev);
}
sleep(1);
UnmountAll(NULL);
Services_Close();
Wpad_Disconnect();
if(header->id[3] == 'P')
GC_SetVideoMode(1, true);
else
GC_SetVideoMode(2, true);
extern void __exception_closeall();
IRQ_Disable();
__IOS_ShutdownSubsystems();
__exception_closeall();
#define LAUNCH() ((void(*)(void))loader_bin)()
LAUNCH();
}
//Start GC using DIOS MIOS
dbg_printf("Setting video mode...\n");
if(CFG.game.video == 0)
{
if(header->id[3] == 'P')
GC_SetVideoMode(1, false);
else
GC_SetVideoMode(2, false);
} else
GC_SetVideoMode(CFG.game.video, false);
dbg_printf("Check DIOS MIOS version...\n");
if(header->magic == GC_GAME_ON_DRIVE)
{
if (CFG.dml == CFG_DML_R51) {
DML_Old_SetOptions(header->path, cheatPath, newCheatPath, CFG.game.ocarina);
} else {
DML_New_SetOptions(header->path, cheatPath, newCheatPath, CFG.game.ocarina, false, CFG.game.mem_card_emu, CFG.game.nodisc, CFG.game.vidtv, CFG.game.video, CFG.game.wide_screen, CFG.game.hooktype, CFG.game.block_ios_reload, CFG.game.screenshot);
}
} else if (CFG.dml >= CFG_DML_1_2) {
DML_New_SetBootDiscOption(cheatPath, newCheatPath, CFG.game.ocarina, CFG.game.mem_card_emu, CFG.game.vidtv, CFG.game.video);
}
if (CFG.game.ntsc_j_patch)
*HW_PPCSPEED = 0x0002A9E0;
dbg_printf("Launching GC game...\n");
UnmountAll(NULL);
Services_Close();
Subsystem_Close();
Wpad_Disconnect();
WII_Initialize();
WII_LaunchTitle(0x0000000100000100ULL);
return;
}
// If fat, open wbfs disc and verfy id as a consistency check
if (!disc) {
if (wbfs_part_fs) {
wbfs_disc_t *d = WBFS_OpenDisc(header->id);
if (!d) {
printf_(gt("ERROR: opening %.6s"), header->id);
printf("\n");
goto out;
}
WBFS_CloseDisc(d);
/*
if (!is_ios_idx_mload(CFG.game.ios_idx))
{
printf(gt("Switching to IOS222 for FAT support."));
printf("\n");
CFG.game.ios_idx = CFG_IOS_222_MLOAD;
cfg_ios_set_idx(CFG.game.ios_idx);
}
*/
}
}
// load stuff before ios reloads & services close
if (CFG.game.clean != CFG_CLEAN_ALL)
ocarina_load_code(header->id);
load_wip_patches(header->id);
load_bca_data(header->id);
load_dolmenu((char*)header->id);
if (!disc) {
ret = get_frag_list(header->id);
if (ret) {
printf_(gt("ERROR: get_frag_list: %d"), ret);
printf("\n");
goto out;
}
}
// stop services (music, gui)
Services_Close();
get_time(&TIME.playstat1);
setPlayStat(header->id); //I'd rather do this after the check, but now you unmount fat before that ;)
get_time(&TIME.playstat2);
if (CFG.game.nand_emu > 0) {
//! Create save game path and title.tmd for not existing saves and nand emulation is activated
CreateSavePath(header);
}
if (CFG.game.alt_dol != 1) {
// unless we're loading alt.dol from sd
// unmount everything
UnmountAll(NULL);
}
if (!disc) {
get_time(&TIME.rios1);
ret = ReloadIOS(1, 1);
get_time(&TIME.rios2);
if (ret < 0) goto out;
if (wbfs_part_fs != PART_FS_WBFS) {
load_dip_249();
}
Block_IOS_Reload();
d2x_return_to_channel();
// verify IOS version
warn_ios_bugs();
if (check_dual_layer(real_size, NULL)) print_dual_layer_note();
//dbg_time1();
/* Set WBFS mode */
ret = Disc_SetWBFS(wbfsDev, header->id);
if (ret) {
printf_(gt("ERROR: SetWBFS: %d"), ret);
printf("\n");
goto out;
}
Setup_NandEmu(CFG.game.nand_emu, CFG.nand_emu_path);
/* Open disc */
ret = Disc_Open();
//dbg_time2("\ndisc open");
//Wpad_WaitButtonsCommon();
bench_io();
if (ret < 0) {
printf_(gt("ERROR: Could not open game! (ret = %d)"), ret);
printf("\n");
goto out;
}
}
// printf("CFG language=%d,Wii language=%x\n",CFG.game.language,CONF_GetLanguage());
// sleep(5);
if ((memcmp("SUK",header->id,3)!=0) && (CFG.game.language == 0)) //Kirby's Return to Dream Land
{
if (((header->id[3]=='W') ||(header->id[3]=='J')) && (CONF_GetLanguage()== 1))
CFG.game.language=1;
if (((header->id[3]=='E') ||(header->id[3]=='P')) && (CONF_GetLanguage()== 0))
CFG.game.language=2;
// printf("GAME ID=%s, language=%d\n",header->id,CFG.game.language);
// sleep(5);
}
if (CFG.game.wide_screen > 0)
{
wiilight(1);
}
switch(CFG.game.language)
{
// 0 = CFG_LANG_CONSOLE
case 0: configbytes[0] = 0xCD; break;
case 1: configbytes[0] = 0x00; break;
case 2: configbytes[0] = 0x01; break;
case 3: configbytes[0] = 0x02; break;
case 4: configbytes[0] = 0x03; break;
case 5: configbytes[0] = 0x04; break;
case 6: configbytes[0] = 0x05; break;
case 7: configbytes[0] = 0x06; break;
case 8: configbytes[0] = 0x07; break;
case 9: configbytes[0] = 0x08; break;
case 10: configbytes[0] = 0x09; break;
}
/* Boot Wii disc */
ret = Disc_WiiBoot(disc);
printf_(gt("Returned! (ret = %d)"), ret);
printf("\n");
out:
printf("\n");
printf_(gt("Press any button to exit..."));
printf("\n");
/* Wait for button */
Wpad_Init();
Wpad_WaitButtonsCommon();
printf("Exiting...");
exit(0);
close:
if (disc) {
printf("\n");
printf_(gt("Stopping DVD..."));
WDVD_StopMotor();
printf(gt("OK"));
header = &gameList[gameSelected];
Gui_DrawCover(header->id);
}
return;
}
void Direct_Launch()
{
int i;
if (!CFG.direct_launch) return;
// enable console in case there is some input
// required when loading game, depends on options, like:
// confirm ocarina, alt_dol=disk (asks for dol)
// but this can be moved to those points and removed from here
// so that console is shown only if needed...
if (CFG.intro) {
Gui_Console_Enable();
}
// confirm_direct_start
//CFG.confirm_start = 0;
//sleep(1);
Con_Clear();
printf(gt("Configurable Loader %s"), CFG_VERSION);
printf("\n\n");
//sleep(1);
for (i=0; i<gameCnt; i++) {
if (strncmp(CFG.launch_discid, (char*)gameList[i].id, 6) == 0) {
gameSelected = i;
Menu_Boot(0);
goto out;
}
}
Gui_Console_Enable();
printf(gt("Auto-start game: %.6s not found!"), CFG.launch_discid);
printf("\n");
printf(gt("Press any button..."));
printf("\n");
Wpad_WaitButtons();
out:
CFG.direct_launch = 0;
}
void Menu_Loop(void)
{
// enable the console if starting with console mode
if (!CFG.gui_start) {
if (!(CFG.direct_launch && !CFG.intro)) {
Gui_Console_Enable();
}
}
// Get MIOS info
CFG.dml = get_miosinfo();
fill_base_array();
/* Device menu */
Menu_Device();
// Direct Launch?
Direct_Launch();
// Start Music
get_time(&TIME.mp31);
Music_Start();
get_time(&TIME.mp32);
get_time(&TIME.guitheme1);
Grx_Init();
get_time(&TIME.guitheme2);
// Init Favorites
Switch_Favorites(CFG.start_favorites || CFG.profile_start_favorites[CFG.current_profile]);
// Init Saved Filters
if (CFG.save_filter) {
switch (CFG.profile_filter[CFG.current_profile]) {
case FILTER_GENRE:
filter_games_set(CFG.profile_filter[CFG.current_profile], CFG.profile_filter_genre[CFG.current_profile]);
break;
case FILTER_FEATURES:
filter_games_set(CFG.profile_filter[CFG.current_profile], CFG.profile_filter_feature[CFG.current_profile]);
break;
case FILTER_CONTROLLER:
filter_games_set(CFG.profile_filter[CFG.current_profile], CFG.profile_filter_controller[CFG.current_profile]);
break;
case FILTER_GAME_TYPE:
filter_games_set(CFG.profile_filter[CFG.current_profile], CFG.profile_filter_gametype[CFG.current_profile]);
break;
case FILTER_SEARCH:
strncpy(search_str, CFG.profile_search_string[CFG.current_profile], 100);
filter_games_set(CFG.profile_filter[CFG.current_profile], CFG.profile_search_field[CFG.current_profile]);
break;
default:
filter_games_set(CFG.profile_filter[CFG.current_profile], 0);
break;
}
}
switch(CFG.select) {
case CFG_SELECT_PREVIOUS:
{
int i;
time_t ret, last=0;
for (i = 0; i < gameCnt; i++) {
if ((ret = getLastPlay(gameList[i].id)) >= last) {
last = ret;
gameSelected = i;
}
}
break;
}
case CFG_SELECT_START:
gameSelected = 0;
break;
case CFG_SELECT_MIDDLE:
gameSelected = (gameCnt-1)/2;
break;
case CFG_SELECT_END:
gameSelected = gameCnt - 1;
break;
case CFG_SELECT_MOST:
{
int i;
u32 ret, last=0;
for (i = 0; i < gameCnt; i++) {
if ((ret = getPlayCount(gameList[i].id)) >= last) {
last = ret;
gameSelected = i;
}
}
break;
}
case CFG_SELECT_LEAST:
{
int i;
u32 ret, last=0xFFFFFFFF;
for (i = 0; i < gameCnt; i++) {
if ((ret = getPlayCount(gameList[i].id)) < last) {
last = ret;
gameSelected = i;
}
}
break;
}
case CFG_SELECT_RANDOM:
gameSelected = (rand() >> 16) % gameCnt;
break;
}
// scroll start list
__Menu_ScrollStartList();
get_time(&TIME.boot2);
//time_stats();
if (CFG.debug) {
Menu_PrintWait();
}
// Clear console
// (so that it doesn't show when switching back from gui)
Con_Clear();
__console_scroll = 0;
// Start GUI
if (CFG.gui_start) {
go_gui = true;
goto skip_list;
}
/* Menu loop */
for (;;) {
/* Clear console */
Con_Clear();
/* Show gamelist */
__Menu_ShowList();
/* Show cover */
__Menu_ShowCover();
//memstat();
skip_list:
/* Controls */
__Menu_Controls();
}
}
// menu support routines
void menu_init(struct Menu *m, int num_opt)
{
memset(m, 0, sizeof(*m));
m->num_opt = num_opt;
}
void menu_begin(struct Menu *m)
{
m->line_count = 0;
}
void menu_init_active(struct Menu *m, char *active, int active_size)
{
m->active = active;
m->active_size = active_size;
if (!active) return;
memset(active, 1, active_size);
}
char m_active(struct Menu *m, int i)
{
if (m->active == NULL) return 1;
if (i >= m->active_size) return 0;
if (i < 0) return 0;
return m->active[i];
}
void menu_jump_active(struct Menu *m)
{
int i;
if (m->current >= m->num_opt) m->current = m->num_opt - 1;
if (m->current < 0) m->current = 0;
if (m_active(m, m->current)) return;
// move to first m->active
for (i=0; i < m->num_opt; i++) {
if (m_active(m, i)) {
m->current = i;
break;
}
}
if (i == m->num_opt) m->current = 0;
}
void menu_move_cap(struct Menu *m)
{
if (m->current >= m->num_opt) m->current = m->num_opt-1;
if (m->current < 0) m->current = 0;
}
void menu_move_wrap(struct Menu *m)
{
if (m->current >= m->num_opt) m->current = 0;
if (m->current < 0) m->current = m->num_opt-1;
if (m->current < 0) m->current = 0;
}
void menu_move_adir(struct Menu *m, int dir)
{
int i;
menu_move_cap(m);
//int n = m->current;
for (i=0; i<m->num_opt; i++) {
m->current += dir;
menu_move_wrap(m);
if (m_active(m, m->current)) break;
}
}
// only move on active lines
void menu_move_active(struct Menu *m, int buttons)
{
if (buttons & WPAD_BUTTON_UP) {
menu_move_adir(m, -1);
}
if (buttons & WPAD_BUTTON_DOWN) {
menu_move_adir(m, 1);
}
}
// simple, move on all lines
void menu_move(struct Menu *m, int buttons)
{
int dir = 0;
menu_move_cap(m);
if (buttons & WPAD_BUTTON_UP) dir = -1;
if (buttons & WPAD_BUTTON_DOWN) dir = 1;
m->current += dir;
menu_move_wrap(m);
}
void menu_print_mark(struct Menu *m)
{
//if (m->active[m->line_count] && m->current == m->line_count) {
if (m->current == m->line_count) {
BgColor(CFG.color_selected_bg);
FgColor(CFG.color_selected_fg);
Con_ClearLine();
} else if (m_active(m, m->line_count)) {
DefaultColor();
} else {
DefaultColor();
FgColor(CFG.color_inactive);
}
char *xx;
//if (m->active[m->line_count] && m->current == m->line_count) {
if (m->current == m->line_count) {
xx = CFG.cursor;
} else {
xx = CFG.cursor_space;
}
printf(" %s ", xx);
}
int menu_mark(struct Menu *m)
{
menu_print_mark(m);
return m->line_count++;
}
// size is number of visible lines on screen
// num_items is number of items in the list
void menu_window_begin(struct Menu *m, int size, int num_items)
{
if (num_items > size + 1)
m->window_size = size;
else
m->window_size = size + 1;
m->window_items = num_items;
m->window_begin = m->line_count;
// adjust window_pos so that selected line is inside the window
// current_pos = position of selected line
// (relative offset from window_begin)
int current_pos = m->current - m->window_begin;
if (current_pos >= m->window_pos + m->window_size) {
if (current_pos > num_items) {
m->window_pos = num_items - m->window_size + 1;
} else {
m->window_pos = current_pos - m->window_size + 1;
}
} else if (current_pos < m->window_pos) {
m->window_pos = current_pos;
}
if (m->window_pos < 0) m->window_pos = 0;
// print page continuation marker
// only if needed
if (m->window_size < m->window_items) {
if (m->window_pos > 0) {
printf(" %s +\n", CFG.cursor_space);
} else {
printf("\n");
}
}
}
bool menu_window_mark(struct Menu *m)
{
int pos = m->line_count - m->window_begin;
if (pos < m->window_pos) {
m->line_count++;
return false;
}
if (pos >= m->window_pos + m->window_size) {
m->line_count++;
return false;
}
menu_mark(m);
return true;
}
void menu_window_end(struct Menu *m, int cols)
{
int pos = m->line_count - m->window_begin;
char str[20] = "";
int len;
printf(" %s ", CFG.cursor_space);
if (pos > m->window_pos + m->window_size) {
printf("+");
} else {
printf(" ");
}
if (m->window_size < m->window_items)
sprintf(str, "%s: %d/%d", gt("page"),
(m->current - m->window_begin) / m->window_size + 1,
(m->window_items-1) / m->window_size + 1);
len = strlen(str);
printf("%*.*s", cols-6-len, cols-6-len, str);
printf("\n");
// debug
//printf("c:%d s:%d b:%d i:%d p:%d\n", m->current, m->window_size,
// m->window_begin, m->window_items, m->window_pos);
}
// indented printf
// will indent also each \n in string
void printf_a(const char *fmt, va_list argp)
{
char strbuf[512];
char *s, *n;
int ret;
ret = vsnprintf(strbuf, sizeof(strbuf), fmt, argp);
if (ret >= sizeof(strbuf)) {
// buffer too small, just print directly
vprintf(fmt, argp);
} else {
// append space also to each \n
s = strbuf;
do {
n = strchr(s, '\n');
if (n) *n = 0; // terminate new line
printf("%s", s);
if (n) {
printf("\n");
s = n + 1;
// only indent if there's more text to print
// don't indent if \n is at the end of string
if (*s == 0) break;
printf("%s", CFG.menu_plus_s);
}
} while (n);
}
}
void printf_(const char *fmt, ...)
{
va_list argp;
printf("%s", CFG.menu_plus_s);
va_start(argp, fmt);
printf_a(fmt, argp);
va_end(argp);
}
void printf_x(const char *fmt, ...)
{
va_list argp;
printf("%s", CFG.menu_plus);
va_start(argp, fmt);
//vprintf(fmt, argp);
printf_a(fmt, argp);
va_end(argp);
}
void printf_h(const char *fmt, ...)
{
va_list argp;
DefaultColor();
FgColor(CFG.color_help);
printf("%s", CFG.menu_plus_s);
va_start(argp, fmt);
//vprintf(fmt, argp);
printf_a(fmt, argp);
va_end(argp);
DefaultColor();
}
int Menu_PrintWait()
{
const char *msg = gt("Press any button to continue...");
printf_h("%s\n", msg);
gecko_printf("%s\n", msg);
// clear button states
gecko_printf("WPAD_Flush(WPAD_CHAN_ALL)\n");
WPAD_Flush(WPAD_CHAN_ALL);
// wait for button press
gecko_printf("Wpad_WaitButtonsCommon()\n");
int button = Wpad_WaitButtonsCommon();
// wait for button release
while (Wpad_HeldButtons()) {
gecko_printf(".");
usleep(10000);
VIDEO_WaitVSync();
}
return button;
}
bool Menu_Confirm(const char *msg)
{
if (msg) {
printf_h(gt("Press %s button to %s."), (button_names[CFG.button_confirm.num]), msg);
} else {
printf_h(gt("Press %s button to continue."), (button_names[CFG.button_confirm.num]));
}
printf("\n");
printf_h(gt("Press %s button to go back."), (button_names[CFG.button_cancel.num]));
printf("\n");
DefaultColor();
WPAD_Flush(WPAD_CHAN_ALL);
for (;;) {
u32 buttons = Wpad_WaitButtonsCommon();
if (buttons & CFG.button_confirm.mask) return true;
if (buttons & CFG.button_cancel.mask) return false;
}
}
void Menu_Plugin(int plugin, char arguments[255][255], int argCnt) {
FILE* file;
u32 entryPoint;
int i = 0;
void *buffer = (void *)0x92000000;
char fname[128];
struct __argv args;
bzero(&args, sizeof(args));
args.argvMagic = ARGV_MAGIC;
args.length = 1;
switch (plugin) {
case PLUGIN_MIGHTY:
snprintf(fname, sizeof(fname), "%s/plugins/mighty.dol", USBLOADER_PATH);
break;
case PLUGIN_NEEK:
snprintf(fname, sizeof(fname), "%s/plugins/neek2o.dol", USBLOADER_PATH);
break;
case PLUGIN_NINTENDONT:
snprintf(fname, sizeof(fname), "/apps/nintendont/boot.dol");
break;
default:
return;
}
args.length += strlen(fname) + 1;
for (i = 0; i < argCnt; i++) {
if (plugin == PLUGIN_NINTENDONT && CFG.nin_cfg_mode) {
args.length += sizeof(NIN_CFG) + 1;
} else {
args.length += strlen(arguments[i]) + 1;
}
}
args.commandLine = (char*)allocate_memory(args.length);
if (!args.commandLine) dbg_printf("failed...\n");
int pos = 0;
strcpy(args.commandLine+pos, fname);
pos += strlen(fname) + 1;
for (i = 0; i < argCnt; i++) {
if (plugin == PLUGIN_NINTENDONT && CFG.nin_cfg_mode) {
memcpy(args.commandLine+pos, ncfg, sizeof(NIN_CFG));
pos += sizeof(NIN_CFG) + 1;
} else {
strcpy(args.commandLine+pos, arguments[i]);
pos += strlen(arguments[i]) + 1;
}
}
args.argc = argCnt + 1;
args.commandLine[args.length - 1] = '\0';
args.argv = &args.commandLine;
args.endARGV = args.argv + 1;
printf_(gt("Loading..%s\n"), fname);
file = fopen(fname, "rb");
if(file == NULL)
{
printf_(gt("Not Found boot.dol!"));
printf("\n");
sleep(5);
return;
}
fseek (file, 0, SEEK_END);
int len = ftell(file);
fseek (file, 0, SEEK_SET);
fread(buffer, 1, len, file);
fclose (file);
if(plugin == PLUGIN_NINTENDONT)
{
const char* magic_str = "$$Version:";
int nin_maj=0,nin_min=0;
for(i = 0; i < len; i += 32)
{
if(memcmp(buffer+i, magic_str, strlen(magic_str)) == 0)
{
sscanf(buffer+i+strlen(magic_str),"%d.%d",&nin_maj,&nin_min);
break;
}
}
printf_(gt("Nintendont Ver. %d.%d\n"),nin_maj,nin_min);
printf("\n");
if (nin_maj < MIN_NIN_MAJ)
{
sleep(5);
return;
}
else if (nin_min < MIN_NIN_MIN)
{
sleep(5);
return;
}
}
entryPoint = load_dol_image_args(buffer, &args);
ReloadIOS(1,1);
d2x_return_to_channel();
UnmountAll(NULL);
Services_Close();
Subsystem_Close();
Wpad_Disconnect();
__IOS_ShutdownSubsystems();
SYS_ResetSystem(SYS_SHUTDOWN,0,0);
entrypoint exec = (entrypoint)entryPoint;
exec();
//__lwp_thread_stopmultitasking((entrypoint)entryPoint);
}
/*
Maybe:
Ocarina (cheats): < Off >
>> Compatibility < Edit >
Favorite: < Yes >
Hide Game: < No >
Cheat Codes: < Manage >
Cover Image: < Download >
< Start Game >
< Remove Game >
< Install Game >
< Global Options >
*/