Skip to content

Commit

Permalink
Add vWii savefile backup/restore
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryuzaki-MrL committed Apr 4, 2017
1 parent c1fcc54 commit ba51de6
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 40 deletions.
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -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:
Expand Down
6 changes: 3 additions & 3 deletions meta/meta.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
<coder>Ryuzaki_MrL</coder>
<version>1.0.0</version>
<release_date>20170404000000</release_date>
<short_description>Wii U Save Manager</short_description>
<long_description>Wii U Save Manager
<short_description>WiiU/vWii Save Manager</short_description>
<long_description>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.</long_description>
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.</long_description>
</app>
140 changes: 110 additions & 30 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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++) {

Expand All @@ -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);

}

Expand All @@ -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);
}
Expand All @@ -142,14 +214,18 @@ 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");
mount_fs("storage_mlc", fsaFd, NULL, "/vol/storage_mlc01");
mount_fs("storage_usb", fsaFd, NULL, "/vol/storage_usb01");

ucls();
Title* titles = loadTitles();
Title* wiiutitles = loadWiiUTitles();
Title* wiititles = loadWiiTitles();

while(1) {

Expand All @@ -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
Expand All @@ -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, "->");
Expand All @@ -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;
Expand Down Expand Up @@ -242,7 +321,8 @@ int Menu_Main(void) {

}

unloadTitles(titles);
unloadTitles(wiiutitles);
unloadTitles(wiititles);

fatUnmount("sd");
fatUnmount("usb");
Expand Down
8 changes: 4 additions & 4 deletions src/savemng.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#include <sys/dirent.h>

#include "savemng.h"

#define BUFFER_SIZE 0x80000
Expand Down Expand Up @@ -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);

Expand All @@ -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);

}
Expand Down
1 change: 1 addition & 0 deletions src/savemng.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef _SAVEMNG_H_
#define _SAVEMNG_H_

#include <sys/dirent.h>
#include <gctypes.h>
#include <fat.h>
#include <iosuhax.h>
Expand Down

0 comments on commit ba51de6

Please sign in to comment.