Added some warnings, Shows "disc" saves

-Now title list is by default sorted by name.
-Instead of only asking the WiiU what titles are installed, now also
goes through save folders to find saves of titles not installed (disc
games).
-Now warns if save folder hasn't been initialized. Run title at least
one time to init save folder and metadata. (Posible to restore without
been initialized but can lead to unwanted behavior.)
-Now warns when entering task selection screen of Haxchi/CBHC DS VC
save. (Restoring or Wiping save could lead to problems.)
This commit is contained in:
GabyPCgeeK
2017-07-16 23:39:34 -04:00
parent f696d3941c
commit ea49f43d11
4 changed files with 161 additions and 38 deletions

View File

@@ -2,8 +2,8 @@
<app version="1">
<name>SaveMii Mod</name>
<coder>Ryuzaki_MrL original (GabyPCgeeK mod)</coder>
<version>1.2.0.mod3</version>
<release_date>20170714000000</release_date>
<version>1.2.0.mod4</version>
<release_date>20170716000000</release_date>
<short_description>WiiU/vWii Save Manager</short_description>
<long_description>WiiU/vWii Save Manager

View File

@@ -12,11 +12,11 @@
#define VERSION_MAJOR 1
#define VERSION_MINOR 2
#define VERSION_MICRO 0
#define VERSION_MOD "mod3"
#define VERSION_MOD "mod4"
u8 slot = 0;
bool allusers = 0, common = 1;
int menu = 0, mode = 0, task = 0, targ = 0, tsort = 0, sorta = 1;
int menu = 0, mode = 0, task = 0, targ = 0, tsort = 1, sorta = 1;
int cursor = 0, scroll = 0;
int cursorb = 0, scrollb = 0;
int titleswiiu = 0, titlesvwii = 0;
@@ -63,15 +63,15 @@ int titleSort(const void *c1, const void *c2)
if (((Title*)c1)->isTitleOnUSB == ((Title*)c2)->isTitleOnUSB)
return 0;
if (((Title*)c1)->isTitleOnUSB)
return 1 * sorta;
if (((Title*)c2)->isTitleOnUSB)
return -1 * sorta;
if (((Title*)c2)->isTitleOnUSB)
return 1 * sorta;
return 0;
} else if (tsort==3) {
if (((Title*)c1)->isTitleOnUSB && !((Title*)c2)->isTitleOnUSB)
return 1 * sorta;
if (!((Title*)c1)->isTitleOnUSB && ((Title*)c2)->isTitleOnUSB)
return -1 * sorta;
if (!((Title*)c1)->isTitleOnUSB && ((Title*)c2)->isTitleOnUSB)
return 1 * sorta;
return strcmp(((Title*)c1)->shortName,((Title*)c2)->shortName) * sorta;
}
@@ -93,22 +93,112 @@ Title* loadWiiUTitles(int run, int fsaFd) {
return NULL;
}
Title* titles = malloc(receivedCount*sizeof(Title));
int usable = receivedCount, j = 0;
Saves* savesl = malloc(receivedCount*sizeof(Saves));
if (!savesl) {
promptError("Out of memory.");
return NULL;
}
for (int i = 0; i < receivedCount; i++) {
char* element = tList+(i*0x61);
savesl[j].highID = *(u32*)(element);
if (savesl[j].highID != 0x00050000) {
usable--;
continue;
}
savesl[j].lowID = *(u32*)(element+4);
savesl[j].dev = !(memcmp(element+0x56, "usb", 4) == 0);
savesl[j].found = false;
j++;
}
savesl = realloc(savesl, usable*sizeof(Saves));
int dirUH, dirNH, foundCount = 0, pos = 0, tNoSave = usable;
for(int i = 0; i <= 1; i++) {
char path[255];
sprintf(path, "/vol/storage_%s01/usr/save/00050000", (i == 0) ? "usb" : "mlc");
if (IOSUHAX_FSA_OpenDir(fsaFd, path, &dirUH) >= 0) {
while (1) {
directoryEntry_s data;
int ret = IOSUHAX_FSA_ReadDir(fsaFd, dirUH, &data);
if (ret != 0)
break;
sprintf(path, "/vol/storage_%s01/usr/save/00050000/%s/user", (i == 0) ? "usb" : "mlc", data.name);
if (checkEntry(path) == 2) {
sprintf(path, "/vol/storage_%s01/usr/save/00050000/%s/meta/meta.xml", (i == 0) ? "usb" : "mlc", data.name);
if (checkEntry(path) == 1) {
for (int i = 0; i < usable; i++) {
if ((savesl[i].highID == 0x00050000) && (strtoul(data.name, NULL, 16) == savesl[i].lowID)) {
savesl[i].found = true;
tNoSave--;
break;
}
}
foundCount++;
}
}
}
IOSUHAX_FSA_CloseDir(fsaFd, dirUH);
}
}
foundCount += tNoSave;
Saves* saves = malloc((foundCount + tNoSave)*sizeof(Saves));
if (!saves) {
promptError("Out of memory.");
return NULL;
}
for(int i = 0; i <= 1; i++) {
char path[255];
sprintf(path, "/vol/storage_%s01/usr/save/00050000", (i == 0) ? "usb" : "mlc");
if (IOSUHAX_FSA_OpenDir(fsaFd, path, &dirUH) >= 0) {
while (1) {
directoryEntry_s data;
int ret = IOSUHAX_FSA_ReadDir(fsaFd, dirUH, &data);
if (ret != 0)
break;
sprintf(path, "/vol/storage_%s01/usr/save/00050000/%s/meta/meta.xml", (i == 0) ? "usb" : "mlc", data.name);
if (checkEntry(path) == 1) {
saves[pos].highID = 0x00050000;
saves[pos].lowID = strtoul(data.name, NULL, 16);
saves[pos].dev = i;
saves[pos].found = false;
pos++;
}
}
IOSUHAX_FSA_CloseDir(fsaFd, dirUH);
}
}
for (int i = 0; i < usable; i++) {
if (!savesl[i].found) {
saves[pos].highID = savesl[i].highID;
saves[pos].lowID = savesl[i].lowID;
saves[pos].dev = savesl[i].dev;
saves[pos].found = true;
pos++;
}
}
Title* titles = malloc(foundCount*sizeof(Title));
if (!titles) {
promptError("Out of memory.");
return NULL;
}
for (int i = 0; i < receivedCount; i++) {
for (int i = 0; i < foundCount; i++) {
int srcFd = -1;
char* element = tList+(i*0x61);
u32 highID = *(u32*)(element), lowID = *(u32*)(element+4);
if (highID!=0x00050000) continue;
bool isTitleOnUSB = (memcmp(element+0x56,"usb",4)==0);
u32 highID = saves[i].highID, lowID = saves[i].lowID;
bool isTitleOnUSB = !saves[i].dev;
char path[255];
memset(path, 0, 255);
if (memcmp(element+0x56,"odd",4)==0) strcpy(path, "/vol/storage_odd_content/meta/meta.xml");
else sprintf(path, "/vol/storage_%s01/usr/title/%08x/%08x/meta/meta.xml", element+0x56, highID, lowID);
if (saves[i].found)
sprintf(path, "/vol/storage_%s01/usr/title/%08x/%08x/meta/meta.xml", isTitleOnUSB ? "usb" : "mlc", highID, lowID);
else
sprintf(path, "/vol/storage_%s01/usr/save/%08x/%08x/meta/meta.xml", isTitleOnUSB ? "usb" : "mlc", highID, lowID);
titles[titleswiiu].saveInit = !saves[i].found;
int ret = IOSUHAX_FSA_OpenFile(fsaFd, path, "rb", &srcFd);
if (ret >= 0) {
@@ -157,6 +247,8 @@ Title* loadWiiUTitles(int run, int fsaFd) {
}
free(savesl);
free(saves);
free(tList);
return titles;
@@ -166,14 +258,12 @@ Title* loadWiiTitles(int fsaFd) {
int dirH;
if (IOSUHAX_FSA_OpenDir(fsaFd, "/vol/storage_slccmpt01/title/00010000", &dirH) < 0) return -1;
while (1) {
directoryEntry_s data;
int ret = IOSUHAX_FSA_ReadDir(fsaFd, dirH, &data);
if (ret != 0)
break;
if (strcmp(data.name, "..") == 0 || strcmp(data.name, ".") == 0) continue;
titlesvwii++;
} IOSUHAX_FSA_RewindDir(fsaFd, dirH);
@@ -190,8 +280,6 @@ Title* loadWiiTitles(int fsaFd) {
if (ret != 0)
break;
if (strcmp(data.name, "..") == 0 || strcmp(data.name, ".") == 0) continue;
int srcFd = -1;
char path[256];
sprintf(path, "/vol/storage_slccmpt01/title/00010000/%s/data/banner.bin", data.name);
@@ -288,6 +376,8 @@ int Menu_Main(void) {
Title* wiititles = loadWiiTitles(fsaFd);
int* versionList = (int*)malloc(0x100*sizeof(int));
qsort(wiiutitles, titleswiiu, sizeof(Title), titleSort);
qsort(wiititles, titlesvwii, sizeof(Title), titleSort);
while(1) {
OSScreenClearBufferEx(0, 0);
@@ -312,7 +402,7 @@ int Menu_Main(void) {
entrycount = count;
for (int i = 0; i < 14; i++) {
if (i+scroll<0 || i+scroll>=count) break;
if (strlen(titles[i+scroll].shortName)) console_print_pos(0, i+2, " %s %s %s", titles[i+scroll].shortName, titles[i+scroll].isTitleOnUSB ? "(USB)" : ((mode == 0) ? "(NAND)" : ""), titles[i+scroll].isTitleDupe ? "[D]" : "");
if (strlen(titles[i+scroll].shortName)) console_print_pos(0, i+2, " %s %s%s%s", titles[i+scroll].shortName, titles[i+scroll].isTitleOnUSB ? "(USB)" : ((mode == 0) ? "(NAND)" : ""), titles[i+scroll].isTitleDupe ? " [D]" : "", titles[i+scroll].saveInit ? "" : " [Not Init]");
else console_print_pos(0, i+2, " %08lx%08lx", titles[i+scroll].highID, titles[i+scroll].lowID);
} console_print_pos(0, 2 + cursor, "->");
} break;
@@ -330,8 +420,6 @@ int Menu_Main(void) {
console_print_pos(0, 7+3, " Copy Savedata to Title in %s", titles[targ].isTitleOnUSB ? "NAND" : "USB");
}
}
console_print_pos(0, 2+3 + cursor, "->");
} break;
case 3: { // Select Options
@@ -403,12 +491,12 @@ int Menu_Main(void) {
if (isPressed(VPAD_BUTTON_R) && (menu==1)) {
tsort = (tsort + 1) % 4;
qsort(wiiutitles, titleswiiu, sizeof(Title), titleSort);
qsort(titles, count, sizeof(Title), titleSort);
}
if (isPressed(VPAD_BUTTON_L) && (menu==1) && (tsort > 0)) {
sorta *= -1;
qsort(wiiutitles, titleswiiu, sizeof(Title), titleSort);
qsort(titles, count, sizeof(Title), titleSort);
}
if (isPressed(VPAD_BUTTON_X) && (menu == 1) && (mode == 0)) {
@@ -441,12 +529,37 @@ int Menu_Main(void) {
cursorb = cursor;
scrollb = scroll;
if (titles[targ].highID==0 || titles[targ].lowID==0) continue;
if ((mode == 0) && (strcmp(titles[targ].shortName, "DONT TOUCH ME") == 0)) {
if (!promptConfirm("Haxchi/CBHC save. Could be dangerous to modify. Continue?") || !promptConfirm("Are you REALLY sure?")) {
cursor = cursorb;
scroll = scrollb;
continue;
}
}
if ((mode == 0) && (!titles[targ].saveInit)) {
if (!promptConfirm("Recommended to run Game at least one time. Continue?") || !promptConfirm("Are you REALLY sure?")) {
cursor = cursorb;
scroll = scrollb;
continue;
}
}
}
if (menu == 2) {
task = cursor;
if (task == 0) {
if (!titles[targ].saveInit) {
promptError("No save to Backup.");
continue;
}
}
if (task == 2) {
allusers = promptConfirm("Delete save from all users?");
wipeSavedata(&titles[targ], allusers, hasCommonSave(&titles[targ], false, false, 0, 0));
allusers = promptConfirm("Delete save from all users? A-Yes B-No");
if (hasCommonSave(&titles[targ], false, false, 0, 0)) {
if (!allusers) promptConfirm("Delete common save? A-Yes B-No");
} else {
common = false;
}
wipeSavedata(&titles[targ], allusers, common); break;
continue;
}
if (task > 2) {
@@ -464,7 +577,12 @@ int Menu_Main(void) {
case 1: restoreSavedata(&titles[targ], slot, allusers, common); break;
case 2:
allusers = promptConfirm("Delete save from all users? A-Yes B-No");
wipeSavedata(&titles[targ], allusers, hasCommonSave(&titles[targ], false, false, 0, 0)); break;
if (hasCommonSave(&titles[targ], false, false, 0, 0)) {
if (!allusers) promptConfirm("Delete common save? A-Yes B-No");
} else {
common = false;
}
wipeSavedata(&titles[targ], allusers, common); break;
case 3: importFromLoadiine(&titles[targ], common, versionList ? versionList[slot] : 0); break;
case 4: exportToLoadiine(&titles[targ], common, versionList ? versionList[slot] : 0); break;
case 5: copySavedata(&titles[targ], &titles[titles[targ].dupeID], allusers, common); break;

View File

@@ -100,20 +100,14 @@ void console_print_pos(int x, int y, const char* format, ...) { // Source: ftpii
}
void console_print_pos_va(int x, int y, const char* format, va_list va) { // Source: ftpiiu
char* tmp = NULL;
if ((vasprintf(&tmp, format, va) >= 0) && tmp) {
if (strlen(tmp) > 79) tmp[79] = 0;
OSScreenPutFontEx(0, x, y, tmp);
OSScreenPutFontEx(1, x, y, tmp);
}
if (tmp) free(tmp);
}
bool promptConfirm(const char* question) {
@@ -526,6 +520,8 @@ void copySavedata(Title* title, Title* titleb, bool allusers, bool common) {
const char* pathb = (isUSBb ? "/vol/storage_usb01/usr/save" : "/vol/storage_mlc01/usr/save");
sprintf(srcPath, "%s/%08x/%08x/%s", path, highID, lowID, "user");
sprintf(dstPath, "%s/%08x/%08x/%s", pathb, highIDb, lowIDb, "user");
createFolder(dstPath);
if (!allusers) {
char usrPath[16];
getUserID(usrPath);
@@ -557,11 +553,9 @@ void backupSavedata(Title* title, u8 slot, bool allusers, bool common) {
char dstPath[PATH_SIZE];
const char* path = (isWii ? "/vol/storage_slccmpt01/title" : (isUSB ? "/vol/storage_usb01/usr/save" : "/vol/storage_mlc01/usr/save"));
sprintf(srcPath, "%s/%08x/%08x/%s", path, highID, lowID, isWii ? "data" : "user");
IOSUHAX_FSA_MakeDir(fsaFd, "/vol/storage_sdcard/wiiu/backups", 0x666);
sprintf(dstPath, "/vol/storage_sdcard/wiiu/backups/%08x%08x", highID, lowID);
IOSUHAX_FSA_MakeDir(fsaFd, dstPath, 0x666);
sprintf(dstPath, "/vol/storage_sdcard/wiiu/backups/%08x%08x/%u", highID, lowID, slot);
IOSUHAX_FSA_MakeDir(fsaFd, dstPath, 0x666);
createFolder(dstPath);
if (!allusers && !isWii) {
char usrPath[16];
getUserID(usrPath);
@@ -604,6 +598,8 @@ void restoreSavedata(Title* title, u8 slot, bool allusers, bool common) {
const char* path = (isWii ? "/vol/storage_slccmpt01/title" : (isUSB ? "/vol/storage_usb01/usr/save" : "/vol/storage_mlc01/usr/save"));
sprintf(srcPath, "/vol/storage_sdcard/wiiu/backups/%08x%08x/%u", highID, lowID, slot);
sprintf(dstPath, "%s/%08x/%08x/%s", path, highID, lowID, isWii ? "data" : "user");
createFolder(dstPath);
if (!allusers && !isWii) {
char usrPath[16];
getUserID(usrPath);
@@ -676,6 +672,7 @@ void importFromLoadiine(Title* title, bool common, int version) {
u32 srcOffset = strlen(srcPath);
getLoadiineUserDir(srcPath, srcPath, usrPath);
sprintf(dstPath, "/vol/storage_%s01/usr/save/%08x/%08x/user", isUSB ? "usb" : "mlc", highID, lowID);
createFolder(dstPath);
u32 dstOffset = strlen(dstPath);
sprintf(dstPath + dstOffset, "/%s", usrPath);
if (DumpDir(srcPath, dstPath) != 0) promptError("Failed to import savedata from loadiine.");

View File

@@ -22,11 +22,19 @@ typedef struct {
u16 listID;
char shortName[256];
char productCode[32];
bool saveInit;
bool isTitleOnUSB;
bool isTitleDupe;
u16 dupeID;
} Title;
typedef struct {
u32 highID;
u32 lowID;
u8 dev;
bool found;
} Saves;
void console_print_pos(int x, int y, const char* format, ...);
bool promptConfirm(const char* question);
void promptError(const char* message, ...);