diff --git a/gui.pnproj b/gui.pnproj index 9c8c1fea..4ff60c09 100644 --- a/gui.pnproj +++ b/gui.pnproj @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/source/menu.cpp b/source/menu.cpp index 1ac93f72..f61607e9 100644 --- a/source/menu.cpp +++ b/source/menu.cpp @@ -698,7 +698,7 @@ int MenuDiscList() { mainWindow->Append(&w); ResumeGui(); - + while (menu == MENU_NONE) { if (idiotFlag==1) { diff --git a/source/prompts/DiscBrowser.cpp b/source/prompts/DiscBrowser.cpp index 11e231bc..02aa8c83 100644 --- a/source/prompts/DiscBrowser.cpp +++ b/source/prompts/DiscBrowser.cpp @@ -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) { diff --git a/source/wad/isfs.c b/source/wad/isfs.c new file mode 100644 index 00000000..abbea717 --- /dev/null +++ b/source/wad/isfs.c @@ -0,0 +1,494 @@ +/* + +libisfs -- a NAND filesystem devoptab library for the Wii + +Copyright (C) 2008 Joseph Jordan +Copyright (C) 2009 Waninkoko + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/source/wad/isfs.h b/source/wad/isfs.h new file mode 100644 index 00000000..2a509197 --- /dev/null +++ b/source/wad/isfs.h @@ -0,0 +1,37 @@ +/* + +libisfs -- a NAND filesystem devoptab library for the Wii + +Copyright (C) 2008 Joseph Jordan + +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 + +#define ISFS_MAXPATHLEN (ISFS_MAXPATH + 1) + +bool ISFS_Mount(); +bool ISFS_Unmount(); + +#endif /* _LIBISFS_H_ */ + diff --git a/source/wad/title.c b/source/wad/title.c index 8db49eb6..85870135 100644 --- a/source/wad/title.c +++ b/source/wad/title.c @@ -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;cnttid >> 32),(u32)(save->tid & 0xFFFFFFFF)))==0) + return 1; + } + return 0; + +} + /*------------------------------------------------------------- from any title deleter diff --git a/source/wad/title.h b/source/wad/title.h index 03a48198..a7ab8073 100644 --- a/source/wad/title.h +++ b/source/wad/title.h @@ -60,6 +60,9 @@ extern "C" { char *__getTitleName(u64 titleid, int language); 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 }