Added stuff to make it even more noob-proof.

Made function to check if a game has save data on the Wii nand.  This can be used to allow/not allow booting certain alt dols.  So far it is only called before 1 game, and it seems to work fine without issue.
This commit is contained in:
giantpune 2009-09-19 05:36:32 +00:00
parent 9393c7f87e
commit ef30d9c6d9
7 changed files with 699 additions and 2 deletions

File diff suppressed because one or more lines are too long

View File

@ -17,6 +17,7 @@
#include "sys.h"
#include "settings/cfg.h"
#include "memory.h"
#include "../wad/title.h"
/*** Extern functions ***/
extern void ResumeGui();
@ -316,6 +317,15 @@ int autoSelectDol(const char *id, bool force) {
int autoSelectDolMenu(const char *id) {
//Metroid Prime Trilogy
//don't let the game start without a save game in the nand
char id4[10];
sprintf(id4,"%c%c%c%c",id[0],id[1],id[2],id[3]);
if (CheckForSave(id4)==0)
{
WindowPrompt(0,"You need to play the game one time to create a save file. Then exit and start it again using the dol of the game you want to use.",tr("Ok"));
return -1;
}
if (strcmp(id,"R3ME01") == 0) {
int choice = WindowPrompt(tr("Select a DOL"), 0, "Metroid Prime", "Metroid Prime 2", "Metroid Prime 3", tr("Default"));
switch (choice) {

494
source/wad/isfs.c Normal file
View File

@ -0,0 +1,494 @@
/*
libisfs -- a NAND filesystem devoptab library for the Wii
Copyright (C) 2008 Joseph Jordan <joe.ftpii@psychlaw.com.au>
Copyright (C) 2009 Waninkoko <waninkoko@gmail.com>
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from
the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1.The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2.Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3.This notice may not be removed or altered from any source distribution.
*/
#include <errno.h>
#include <ogc/isfs.h>
#include <ogc/lwp_watchdog.h>
#include <ogcsys.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/dir.h>
#include <sys/iosupport.h>
#include "isfs.h"
#define DEVICE_NAME "isfs"
#define FLAG_DIR 1
#define DIR_SEPARATOR '/'
#define SECTOR_SIZE 0x800
#define BUFFER_SIZE 0x8000
typedef struct DIR_ENTRY_STRUCT {
char *name;
char *abspath;
u32 size;
u8 flags;
u32 fileCount;
struct DIR_ENTRY_STRUCT *children;
} DIR_ENTRY;
typedef struct {
DIR_ENTRY *entry;
s32 isfs_fd;
bool inUse;
} FILE_STRUCT;
typedef struct {
DIR_ENTRY *entry;
u32 index;
bool inUse;
} DIR_STATE_STRUCT;
static char rw_buffer[BUFFER_SIZE] __attribute__((aligned(32)));
static DIR_ENTRY *root = NULL;
static DIR_ENTRY *current = NULL;
static s32 dotab_device = -1;
static bool is_dir(DIR_ENTRY *entry) {
return entry->flags & FLAG_DIR;
}
static bool invalid_drive_specifier(const char *path) {
if (strchr(path, ':') == NULL) return false;
int namelen = strlen(DEVICE_NAME);
if (!strncmp(DEVICE_NAME, path, namelen) && path[namelen] == ':') return false;
return true;
}
static DIR_ENTRY *entry_from_path(const char *path) {
if (invalid_drive_specifier(path)) return NULL;
if (strchr(path, ':') != NULL) path = strchr(path, ':') + 1;
DIR_ENTRY *entry;
bool found = false;
bool notFound = false;
const char *pathPosition = path;
const char *pathEnd = strchr(path, '\0');
if (pathPosition[0] == DIR_SEPARATOR) {
entry = root;
while (pathPosition[0] == DIR_SEPARATOR) pathPosition++;
if (pathPosition >= pathEnd) found = true;
} else {
entry = current;
}
if (entry == root && !strcmp(".", pathPosition)) found = true;
DIR_ENTRY *dir = entry;
while (!found && !notFound) {
const char *nextPathPosition = strchr(pathPosition, DIR_SEPARATOR);
size_t dirnameLength;
if (nextPathPosition != NULL) dirnameLength = nextPathPosition - pathPosition;
else dirnameLength = strlen(pathPosition);
if (dirnameLength >= ISFS_MAXPATHLEN) return NULL;
u32 fileIndex = 0;
while (fileIndex < dir->fileCount && !found && !notFound) {
entry = &dir->children[fileIndex];
if (dirnameLength == strnlen(entry->name, ISFS_MAXPATHLEN - 1) && !strncasecmp(pathPosition, entry->name, dirnameLength)) found = true;
if (found && !is_dir(entry) && nextPathPosition) found = false;
if (!found) fileIndex++;
}
if (fileIndex >= dir->fileCount) {
notFound = true;
found = false;
} else if (!nextPathPosition || nextPathPosition >= pathEnd) {
found = true;
} else if (is_dir(entry)) {
dir = entry;
pathPosition = nextPathPosition;
while (pathPosition[0] == DIR_SEPARATOR) pathPosition++;
if (pathPosition >= pathEnd) found = true;
else found = false;
}
}
if (found && !notFound) return entry;
return NULL;
}
static int _ISFS_open_r(struct _reent *r, void *fileStruct, const char *path, int flags, int mode) {
FILE_STRUCT *file = (FILE_STRUCT *)fileStruct;
DIR_ENTRY *entry = entry_from_path(path);
if (!entry) {
r->_errno = ENOENT;
return -1;
} else if (is_dir(entry)) {
r->_errno = EISDIR;
return -1;
}
int omode = 0;
if (mode & O_RDONLY)
omode |= ISFS_OPEN_READ;
if (mode & O_WRONLY)
omode |= ISFS_OPEN_WRITE;
if (mode & O_RDWR)
omode |= ISFS_OPEN_RW;
if (mode & O_CREAT) {
int user = 0;
int group = 0;
int other = 0;
if (flags & S_IRUSR)
user |= ISFS_OPEN_READ;
if (flags & S_IWUSR)
user |= ISFS_OPEN_WRITE;
if (flags & S_IRGRP)
group |= ISFS_OPEN_READ;
if (flags & S_IWGRP)
group |= ISFS_OPEN_WRITE;
if (flags & S_IROTH)
other |= ISFS_OPEN_READ;
if (flags & S_IWOTH)
other |= ISFS_OPEN_WRITE;
ISFS_CreateFile(entry->abspath, 0, user, group, other);
}
file->entry = entry;
file->inUse = true;
file->isfs_fd = ISFS_Open(entry->abspath, omode);
if (file->isfs_fd < 0) {
r->_errno = -file->isfs_fd;
return -1;
}
return (int)file;
}
static int _ISFS_close_r(struct _reent *r, int fd) {
FILE_STRUCT *file = (FILE_STRUCT *)fd;
if (!file->inUse) {
r->_errno = EBADF;
return -1;
}
file->inUse = false;
s32 ret = ISFS_Close(file->isfs_fd);
if (ret < 0) {
r->_errno = -ret;
return -1;
}
return 0;
}
static int _ISFS_write_r(struct _reent *r, int fd, const char *ptr, size_t len) {
FILE_STRUCT *file = (FILE_STRUCT *)fd;
if (!file->inUse) {
r->_errno = EBADF;
return -1;
}
if (len <= 0) {
return 0;
}
memcpy(rw_buffer, ptr, len);
s32 ret = ISFS_Write(file->isfs_fd, rw_buffer, len);
if (ret < 0) {
r->_errno = -ret;
return -1;
} else if (ret < len) {
r->_errno = EOVERFLOW;
}
return ret;
}
static int _ISFS_read_r(struct _reent *r, int fd, char *ptr, size_t len) {
FILE_STRUCT *file = (FILE_STRUCT *)fd;
if (!file->inUse) {
r->_errno = EBADF;
return -1;
}
if (len <= 0) {
return 0;
}
s32 ret = ISFS_Read(file->isfs_fd, rw_buffer, len);
if (ret < 0) {
r->_errno = -ret;
return -1;
} else if (ret < len) {
r->_errno = EOVERFLOW;
}
memcpy(ptr, rw_buffer, ret);
return ret;
}
static off_t _ISFS_seek_r(struct _reent *r, int fd, off_t pos, int dir) {
FILE_STRUCT *file = (FILE_STRUCT *)fd;
if (!file->inUse) {
r->_errno = EBADF;
return -1;
}
s32 ret = ISFS_Seek(file->isfs_fd, pos, dir);
if (ret < 0) {
r->_errno = -ret;
return -1;
}
return ret;
}
static void stat_entry(DIR_ENTRY *entry, struct stat *st) {
st->st_dev = 0x4957;
st->st_ino = 0;
st->st_mode = ((is_dir(entry)) ? S_IFDIR : S_IFREG) | (S_IRUSR | S_IRGRP | S_IROTH);
st->st_nlink = 1;
st->st_uid = 1;
st->st_gid = 2;
st->st_rdev = st->st_dev;
st->st_size = entry->size;
st->st_atime = 0;
st->st_spare1 = 0;
st->st_mtime = 0;
st->st_spare2 = 0;
st->st_ctime = 0;
st->st_spare3 = 0;
st->st_blksize = SECTOR_SIZE;
st->st_blocks = (entry->size + SECTOR_SIZE - 1) / SECTOR_SIZE;
st->st_spare4[0] = 0;
st->st_spare4[1] = 0;
}
static int _ISFS_fstat_r(struct _reent *r, int fd, struct stat *st) {
FILE_STRUCT *file = (FILE_STRUCT *)fd;
if (!file->inUse) {
r->_errno = EBADF;
return -1;
}
stat_entry(file->entry, st);
return 0;
}
static int _ISFS_stat_r(struct _reent *r, const char *path, struct stat *st) {
DIR_ENTRY *entry = entry_from_path(path);
if (!entry) {
r->_errno = ENOENT;
return -1;
}
stat_entry(entry, st);
return 0;
}
static int _ISFS_chdir_r(struct _reent *r, const char *path) {
DIR_ENTRY *entry = entry_from_path(path);
if (!entry) {
r->_errno = ENOENT;
return -1;
} else if (!is_dir(entry)) {
r->_errno = ENOTDIR;
return -1;
}
return 0;
}
static int _ISFS_mkdir_r(struct _reent *r, const char *path, int mode) {
DIR_ENTRY *entry = entry_from_path(path);
if (entry) {
r->_errno = ENOENT;
return -1;
}
int other = (mode % 10);
int group = (mode / 10) % 10;
int user = (mode / 100) % 10;
s32 ret = ISFS_CreateDir(path, 0, user >> 1, group >> 1, other >> 1);
if (ret < 0) {
r->_errno = -ret;
return -1;
}
return 0;
}
static DIR_ITER *_ISFS_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path) {
DIR_STATE_STRUCT *state = (DIR_STATE_STRUCT *)(dirState->dirStruct);
state->entry = entry_from_path(path);
if (!state->entry) {
r->_errno = ENOENT;
return NULL;
} else if (!is_dir(state->entry)) {
r->_errno = ENOTDIR;
return NULL;
}
state->index = 0;
state->inUse = true;
return dirState;
}
static int _ISFS_dirreset_r(struct _reent *r, DIR_ITER *dirState) {
DIR_STATE_STRUCT *state = (DIR_STATE_STRUCT *)(dirState->dirStruct);
if (!state->inUse) {
r->_errno = EBADF;
return -1;
}
state->index = 0;
return 0;
}
static int _ISFS_dirnext_r(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *st) {
DIR_STATE_STRUCT *state = (DIR_STATE_STRUCT *)(dirState->dirStruct);
if (!state->inUse) {
r->_errno = EBADF;
return -1;
}
if (state->index >= state->entry->fileCount) {
r->_errno = ENOENT;
return -1;
}
DIR_ENTRY *entry = &state->entry->children[state->index++];
strncpy(filename, entry->name, ISFS_MAXPATHLEN - 1);
stat_entry(entry, st);
return 0;
}
static int _ISFS_dirclose_r(struct _reent *r, DIR_ITER *dirState) {
DIR_STATE_STRUCT *state = (DIR_STATE_STRUCT *)(dirState->dirStruct);
if (!state->inUse) {
r->_errno = EBADF;
return -1;
}
state->inUse = false;
return 0;
}
static const devoptab_t dotab_isfs = {
DEVICE_NAME,
sizeof(FILE_STRUCT),
_ISFS_open_r,
_ISFS_close_r,
_ISFS_write_r,
_ISFS_read_r,
_ISFS_seek_r,
_ISFS_fstat_r,
_ISFS_stat_r,
NULL,
NULL,
_ISFS_chdir_r,
NULL,
_ISFS_mkdir_r,
sizeof(DIR_STATE_STRUCT),
_ISFS_diropen_r,
_ISFS_dirreset_r,
_ISFS_dirnext_r,
_ISFS_dirclose_r,
NULL
};
static DIR_ENTRY *add_child_entry(DIR_ENTRY *dir, const char *name) {
DIR_ENTRY *newChildren = realloc(dir->children, (dir->fileCount + 1) * sizeof(DIR_ENTRY));
if (!newChildren) return NULL;
bzero(newChildren + dir->fileCount, sizeof(DIR_ENTRY));
dir->children = newChildren;
DIR_ENTRY *child = &dir->children[dir->fileCount++];
child->name = strdup(name);
if (!child->name) return NULL;
child->abspath = malloc(strlen(dir->abspath) + (dir != root) + strlen(name) + 1);
if (!child->abspath) return NULL;
sprintf(child->abspath, "%s/%s", dir == root ? "" : dir->abspath, name);
return child;
}
static bool read_recursive(DIR_ENTRY *parent) {
u32 fileCount;
s32 ret = ISFS_ReadDir(parent->abspath, NULL, &fileCount);
if (ret != ISFS_OK) {
s32 fd = ISFS_Open(parent->abspath, ISFS_OPEN_READ);
if (fd >= 0) {
static fstats st __attribute__((aligned(32)));
if (ISFS_GetFileStats(fd, &st) == ISFS_OK) parent->size = st.file_length;
ISFS_Close(fd);
}
return true;
}
parent->flags = FLAG_DIR;
if (fileCount > 0) {
if ((ISFS_MAXPATHLEN * fileCount) > BUFFER_SIZE) return false;
ret = ISFS_ReadDir(parent->abspath, rw_buffer, &fileCount);
if (ret != ISFS_OK) return false;
u32 fileNum;
char *name = rw_buffer;
for (fileNum = 0; fileNum < fileCount; fileNum++) {
DIR_ENTRY *child = add_child_entry(parent, name);
if (!child) return false;
name += strlen(name) + 1;
}
for (fileNum = 0; fileNum < fileCount; fileNum++)
if (!read_recursive(parent->children + fileNum))
return false;
}
return true;
}
static bool read_isfs() {
root = malloc(sizeof(DIR_ENTRY));
if (!root) return false;
bzero(root, sizeof(DIR_ENTRY));
current = root;
root->name = strdup("/");
if (!root->name) return false;
root->abspath = strdup("/");
if (!root->abspath) return false;
return read_recursive(root);
}
static void cleanup_recursive(DIR_ENTRY *entry) {
u32 i;
for (i = 0; i < entry->fileCount; i++) cleanup_recursive(&entry->children[i]);
if (entry->children) free(entry->children);
if (entry->name) free(entry->name);
if (entry->abspath) free(entry->abspath);
}
bool ISFS_Mount() {
ISFS_Unmount();
bool success = read_isfs() && (dotab_device = AddDevice(&dotab_isfs)) >= 0;
if (!success) ISFS_Unmount();
return success;
}
bool ISFS_Unmount() {
if (root) {
cleanup_recursive(root);
free(root);
root = NULL;
}
current = root;
if (dotab_device >= 0) {
dotab_device = -1;
return !RemoveDevice(DEVICE_NAME ":");
}
return true;
}

37
source/wad/isfs.h Normal file
View File

@ -0,0 +1,37 @@
/*
libisfs -- a NAND filesystem devoptab library for the Wii
Copyright (C) 2008 Joseph Jordan <joe.ftpii@psychlaw.com.au>
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from
the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1.The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2.Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3.This notice may not be removed or altered from any source distribution.
*/
#ifndef _LIBISFS_H
#define _LIBISFS_H
#include <ogc/isfs.h>
#define ISFS_MAXPATHLEN (ISFS_MAXPATH + 1)
bool ISFS_Mount();
bool ISFS_Unmount();
#endif /* _LIBISFS_H_ */

View File

@ -9,6 +9,7 @@
#include "../settings/cfg.h"
#include "fatmounter.h"
#include "id.h"
#include "isfs.h"
#define MAX_TITLES 256
@ -169,6 +170,8 @@ s32 Title_GetSysVersion(u64 tid, u64 *outbuf) {
return 0;
}
s32 Title_GetSize(u64 tid, u32 *outbuf) {
signed_blob *p_tmd = NULL;
tmd *tmd_data = NULL;
@ -317,6 +320,127 @@ s32 Uninstall_DeleteTicket(u32 title_u, u32 title_l) {
return ret;
}
//////savegame shit, from waninkoko. modified for use in this project
/* Savegame structure */
struct savegame {
/* Title name */
char name[65];
/* Title ID */
u64 tid;
};
s32 Savegame_CheckTitle(const char *path)
{
FILE *fp = NULL;
char filepath[128];
/* Generate filepath */
sprintf(filepath, "%s/banner.bin", path);
/* Try to open banner */
fp = fopen(filepath, "rb");
if (!fp)
return -1;
/* Close file */
fclose(fp);
return 0;
}
s32 Savegame_GetNandPath(u64 tid, char *outbuf)
{
s32 ret;
char buffer[1024] ATTRIBUTE_ALIGN(32);
/* Get data directory */
ret = ES_GetDataDir(tid, buffer);
if (ret < 0)
return ret;
/* Generate NAND directory */
sprintf(outbuf, "isfs:%s", buffer);
return 0;
}
s32 __Menu_GetNandSaves(struct savegame **outbuf, u32 *outlen)
{
struct savegame *buffer = NULL;
u64 *titleList = NULL;
u32 titleCnt;
u32 cnt, idx;
s32 ret;
/* Get title list */
ret = Title_GetList(&titleList, &titleCnt);
if (ret < 0)
return ret;
/* Allocate memory */
buffer = malloc(sizeof(struct savegame) * titleCnt);
if (!buffer) {
ret = -1;
goto out;
}
/* Copy titles */
for (cnt = idx = 0; idx < titleCnt; idx++) {
u64 tid = titleList[idx];
char savepath[128];
/* Generate dirpath */
Savegame_GetNandPath(tid, savepath);
/* Check for title savegame */
ret = Savegame_CheckTitle(savepath);
if (!ret) {
struct savegame *save = &buffer[cnt++];
/* Set title ID */
save->tid = tid;
}
}
/* Set values */
*outbuf = buffer;
*outlen = cnt;
/* Success */
ret = 0;
out:
/* Free memory */
if (titleList)
free(titleList);
return ret;
}
s32 __Menu_EntryCmp(const void *p1, const void *p2)
{
struct savegame *s1 = (struct savegame *)p1;
struct savegame *s2 = (struct savegame *)p2;
/* Compare entries */
return strcmp(s1->name, s2->name);
}
s32 __Menu_RetrieveList(struct savegame **outbuf, u32 *outlen)
{
s32 ret;
ret = __Menu_GetNandSaves(outbuf, outlen);
if (ret >= 0)
qsort(*outbuf, *outlen, sizeof(struct savegame), __Menu_EntryCmp);
return ret;
}
//carefull when using this function
//it will force remove stuff even if something fails
s32 Uninstall_FromTitle(const u64 tid) {
@ -586,6 +710,35 @@ char *titleText(u32 kind, u32 title) {
return text;
}
//giantpune's magic function to check for game saves
//give a ID4 of a game and returns 1 if the game has save data, 0 if not, or <0 for errors
int CheckForSave(const char *gameID)
{
if (ISFS_Initialize()<0)
return -1;
if (!ISFS_Mount())
return -2;
struct savegame *saveList = NULL;
u32 saveCnt;
u32 cnt;
if (__Menu_RetrieveList(&saveList, &saveCnt)<0)
return -3;
for (cnt=0;cnt<saveCnt;cnt++)
{
struct savegame *save = &saveList[cnt];
if (strcmp(gameID,titleText((u32)(save->tid >> 32),(u32)(save->tid & 0xFFFFFFFF)))==0)
return 1;
}
return 0;
}
/*-------------------------------------------------------------
from any title deleter

View File

@ -61,6 +61,9 @@ extern "C" {
s32 Uninstall_FromTitle(const u64 tid);
//check for a game save present on nand based on game ID
int CheckForSave(const char *gameID);
#ifdef __cplusplus
}
#endif