From ba51de6c57032c361e3e41a49cbf7a5da56b6f2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1zaro=20Vieira?= Date: Tue, 4 Apr 2017 03:03:22 -0300 Subject: [PATCH] Add vWii savefile backup/restore --- README.md | 5 +- meta/meta.xml | 6 +-- src/main.c | 140 +++++++++++++++++++++++++++++++++++++++----------- src/savemng.c | 8 +-- src/savemng.h | 1 + 5 files changed, 120 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index e33c896..775ac56 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,15 @@ # savemii -Wii U Save Manager +WiiU/vWii Save Manager **You need to run iosuhax/mocha cfw first in order for this homebrew to work.** -This homebrew allows you to backup your Wii U savegames to the SD card and also restore them. +This homebrew allows you to backup your Wii U and vWii savegames to the SD card and also restore them. Up to 256 backups can be made per title. Backups are stored in sd:/wiiu/apps/savemii/backups. Please report any issues that may occur. TODO: -- add vWii support - installable package Credits: diff --git a/meta/meta.xml b/meta/meta.xml index 7b1c63f..2943ec7 100644 --- a/meta/meta.xml +++ b/meta/meta.xml @@ -4,10 +4,10 @@ Ryuzaki_MrL 1.0.0 20170404000000 - Wii U Save Manager - Wii U Save Manager + WiiU/vWii Save Manager + WiiU/vWii Save Manager You need to run iosuhax/mocha cfw first in order for this homebrew to work. -This homebrew allows you to backup your Wii U savegames to the SD card and also restore them. Up to 256 backups can be made per title. Backups are stored in sd:/wiiu/apps/savemii/backups. +This homebrew allows you to backup your Wii U and vWii savegames to the SD card and also restore them. Up to 256 backups can be made per title. Backups are stored in sd:/wiiu/apps/savemii/backups. \ No newline at end of file diff --git a/src/main.c b/src/main.c index 08eff4b..d5d3c8e 100644 --- a/src/main.c +++ b/src/main.c @@ -16,7 +16,7 @@ u8 slot = 0; int menu = 0, mode = 0, task = 0, targ = 0; int cursor = 0, scroll = 0; -int titlecount = 0; +int titleswiiu = 0, titlesvwii = 0; typedef struct { u32 highID; @@ -57,7 +57,7 @@ void MCPHookClose() { mcp_hook_fd = -1; } -Title* loadTitles() { +Title* loadWiiUTitles() { int mcp_handle = MCP_Open(); int count = MCP_TitleCount(mcp_handle); @@ -69,6 +69,10 @@ Title* loadTitles() { MCP_Close(mcp_handle); Title* titles = malloc(receivedCount*sizeof(Title)); + if (!titles) { + promptError("Out of memory."); + return NULL; + } for (int i = 0; i < receivedCount; i++) { @@ -86,31 +90,39 @@ Title* loadTitles() { size_t xmlSize = ftell(xmlFile); fseek(xmlFile, 0, SEEK_SET); char* xmlBuf = malloc(xmlSize+1); - memset(xmlBuf, 0, xmlSize+1); - fread(xmlBuf, 1, xmlSize, xmlFile); - fclose(xmlFile); - - xmlDocPtr tmp = xmlReadMemory(xmlBuf, xmlSize, "meta.xml", "utf-8", 0); - xmlNode* root_element = xmlDocGetRootElement(tmp); - xmlNode* cur_node = NULL; - for (cur_node = root_element->children; cur_node; cur_node = cur_node->next) { - if ( - (cur_node->type != XML_ELEMENT_NODE) || - (memcmp(cur_node->name, "shortname_en", 13) != 0) || - (xmlNodeGetContent(cur_node) == NULL) || - (!strlen((char*)xmlNodeGetContent(cur_node))) - ) continue; - strcpy(titles[titlecount].shortname, (char*)xmlNodeGetContent(cur_node)); - } + if (xmlBuf) { + memset(xmlBuf, 0, xmlSize+1); + fread(xmlBuf, 1, xmlSize, xmlFile); + fclose(xmlFile); + + xmlDocPtr tmp = xmlReadMemory(xmlBuf, xmlSize, "meta.xml", "utf-8", 0); + xmlNode* root_element = xmlDocGetRootElement(tmp); + xmlNode* cur_node = NULL; + for (cur_node = root_element->children; cur_node; cur_node = cur_node->next) { + if ( + (cur_node->type != XML_ELEMENT_NODE) || + (memcmp(cur_node->name, "shortname_en", 13) != 0) || + (xmlNodeGetContent(cur_node) == NULL) || + (!strlen((char*)xmlNodeGetContent(cur_node))) + ) continue; + strcpy(titles[titleswiiu].shortname, (char*)xmlNodeGetContent(cur_node)); + } - xmlFreeDoc(tmp); - free(xmlBuf); + xmlFreeDoc(tmp); + free(xmlBuf); + } } - titles[titlecount].highID = highID; - titles[titlecount].lowID = lowID; - titles[titlecount].isTitleOnUSB = isTitleOnUSB; - titlecount++; + titles[titleswiiu].highID = highID; + titles[titleswiiu].lowID = lowID; + titles[titleswiiu].isTitleOnUSB = isTitleOnUSB; + titleswiiu++; + + OSScreenClearBufferEx(0, 0); + OSScreenClearBufferEx(1, 0); + console_print_pos(0, 0, "Loaded %i Wii U titles.", titleswiiu); + OSScreenFlipBuffersEx(0); + OSScreenFlipBuffersEx(1); } @@ -119,6 +131,66 @@ Title* loadTitles() { } +Title* loadWiiTitles() { + + struct dirent *dirent = NULL; + DIR *dir = NULL; + + dir = opendir("slccmpt01:/title/00010000"); + if (dir == NULL) { + promptError("Failed to open directory."); + return NULL; + } + + while ((dirent = readdir(dir)) != 0) { + if(strcmp(dirent->d_name, "..")==0 || strcmp(dirent->d_name, ".")==0) continue; + titlesvwii++; + } rewinddir(dir); + + Title* titles = malloc(titlesvwii*sizeof(Title)); + if (!titles) { + promptError("Out of memory."); + return NULL; + } + + int i = 0; + while ((dirent = readdir(dir)) != 0) { + + if(strcmp(dirent->d_name, "..")==0 || strcmp(dirent->d_name, ".")==0) continue; + + char path[256]; + sprintf(path, "slccmpt01:/title/00010000/%s/data/banner.bin", dirent->d_name); + FILE* bnrFile = fopen(path, "rb"); + if (bnrFile) { + fseek(bnrFile, 0x20, SEEK_SET); + u16* bnrBuf = (u16*)malloc(0x40); + if (bnrBuf) { + fread(bnrBuf, 0x02, 0x20, bnrFile); + fclose(bnrFile); + for (int j = 0, k = 0; j < 0x20; j++) { + titles[i].shortname[k++] = (char)bnrBuf[j]; + } + free(bnrBuf); + } + } + + titles[i].highID = 0x00010000; + titles[i].lowID = strtoul(dirent->d_name, NULL, 16); + i++; + + OSScreenClearBufferEx(0, 0); + OSScreenClearBufferEx(1, 0); + console_print_pos(0, 1, "Loaded %i Wii titles.", i); + OSScreenFlipBuffersEx(0); + OSScreenFlipBuffersEx(1); + + } + + closedir(dir); + return titles; + +} + void unloadTitles(Title* titles) { free(titles); } @@ -142,6 +214,9 @@ int Menu_Main(void) { int fsaFd = IOSUHAX_FSA_Open(); if (fsaFd < 0) { promptError("IOSUHAX_FSA_Open failed."); + if (mcp_hook_fd >= 0) MCPHookClose(); + else IOSUHAX_Close(); + return EXIT_SUCCESS; } mount_fs("slccmpt01", fsaFd, "/dev/slccmpt01", "/vol/storage_slccmpt01"); @@ -149,7 +224,8 @@ int Menu_Main(void) { mount_fs("storage_usb", fsaFd, NULL, "/vol/storage_usb01"); ucls(); - Title* titles = loadTitles(); + Title* wiiutitles = loadWiiUTitles(); + Title* wiititles = loadWiiTitles(); while(1) { @@ -159,10 +235,13 @@ int Menu_Main(void) { console_print_pos(0, 0, "SaveMii v%u.%u.%u", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO); console_print_pos(0, 1, "--------------------------------------------------"); + Title* titles = mode ? wiititles : wiiutitles; + int count = mode ? titlesvwii : titleswiiu; + switch(menu) { case 0: { // Main Menu console_print_pos(0, 2, " Wii U Save Management"); - // console_print_pos(0, 3, " vWii Save Management"); + console_print_pos(0, 3, " vWii Save Management"); console_print_pos(0, 2 + cursor, "->"); } break; case 1: { // WiiU/vWii Save Management @@ -171,9 +250,9 @@ int Menu_Main(void) { // console_print_pos(0, 4, " Wipe savedata"); console_print_pos(0, 2 + cursor, "->"); } break; - case 2: { // vWii/Wii U Title List + case 2: { // Wii/Wii U Title List for (int i = 0; i < 14; i++) { - if (i+scroll<0 || i+scroll>=titlecount) break; + if (i+scroll<0 || i+scroll>=count) break; if (strlen(titles[i+scroll].shortname)) console_print_pos(0, i+2, " %s", titles[i+scroll].shortname); else console_print_pos(0, i+2, " %08lx%08lx", titles[i+scroll].highID, titles[i+scroll].lowID); } console_print_pos(0, 2 + cursor, "->"); @@ -193,7 +272,7 @@ int Menu_Main(void) { updatePressedButtons(); updateHeldButtons(); - int entrycount = ((menu==0) ? 1 : ((menu==1) ? 2 : titlecount)); + int entrycount = ((menu==0) ? 2 : ((menu==1) ? 2 : count)); if (isPressed(VPAD_BUTTON_DOWN) || isHeld(VPAD_BUTTON_DOWN)) { if (entrycount<=14) cursor = (cursor + 1) % entrycount; @@ -242,7 +321,8 @@ int Menu_Main(void) { } - unloadTitles(titles); + unloadTitles(wiiutitles); + unloadTitles(wiititles); fatUnmount("sd"); fatUnmount("usb"); diff --git a/src/savemng.c b/src/savemng.c index 64afdbe..2c5accf 100644 --- a/src/savemng.c +++ b/src/savemng.c @@ -1,5 +1,3 @@ -#include - #include "savemng.h" #define BUFFER_SIZE 0x80000 @@ -129,7 +127,8 @@ void backupSavedata(u32 highID, u32 lowID, bool isUSB, int slot) { char srcPath[256]; char dstPath[256]; - sprintf(srcPath, "storage_%s:/usr/save/%08x/%08x/user", isUSB ? "usb" : "mlc", highID, lowID); + const char* path = ((highID==0x00010000) ? "slccmpt01:/title" : (isUSB ? "storage_usb:/usr/save" : "storage_mlc:/usr/save")); + sprintf(srcPath, "%s/%08x/%08x/%s", path, highID, lowID, (highID==0x00010000) ? "data" : "user"); sprintf(dstPath, "sd:/wiiu/apps/savemii/backups/%08x%08x/%i", highID, lowID, slot); DumpDir(srcPath, dstPath); @@ -139,8 +138,9 @@ void restoreSavedata(u32 highID, u32 lowID, bool isUSB, int slot) { char srcPath[256]; char dstPath[256]; + const char* path = ((highID==0x00010000) ? "slccmpt01:/title" : (isUSB ? "storage_usb:/usr/save" : "storage_mlc:/usr/save")); sprintf(srcPath, "sd:/wiiu/apps/savemii/backups/%08x%08x/%i", highID, lowID, slot); - sprintf(dstPath, "storage_%s:/usr/save/%08x/%08x/user", isUSB ? "usb" : "mlc", highID, lowID); + sprintf(dstPath, "%s/%08x/%08x/%s", path, highID, lowID, (highID==0x00010000) ? "data" : "user"); DumpDir(srcPath, dstPath); } diff --git a/src/savemng.h b/src/savemng.h index 69955ba..1e2be18 100644 --- a/src/savemng.h +++ b/src/savemng.h @@ -1,6 +1,7 @@ #ifndef _SAVEMNG_H_ #define _SAVEMNG_H_ +#include #include #include #include