diff --git a/meta/meta.xml b/meta/meta.xml index bdb68d4..c235fb2 100644 --- a/meta/meta.xml +++ b/meta/meta.xml @@ -2,8 +2,8 @@ SaveMii Mod Ryuzaki_MrL original (GabyPCgeeK mod) - 1.2.0.mod3 - 20170714000000 + 1.2.0.mod4 + 20170716000000 WiiU/vWii Save Manager WiiU/vWii Save Manager diff --git a/src/main.c b/src/main.c index 1c097d0..fcc5a51 100644 --- a/src/main.c +++ b/src/main.c @@ -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; diff --git a/src/savemng.c b/src/savemng.c index bf03d6f..7603982 100644 --- a/src/savemng.c +++ b/src/savemng.c @@ -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."); diff --git a/src/savemng.h b/src/savemng.h index f0ed349..b1a357d 100644 --- a/src/savemng.h +++ b/src/savemng.h @@ -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, ...);