From f696d3941cbc1ada351441ea39c494728fc54795 Mon Sep 17 00:00:00 2001 From: GabyPCgeeK Date: Sat, 15 Jul 2017 00:24:33 -0400 Subject: [PATCH] Major Changes Changelog No longer uses libxml2, uses string searching and manipulation (Not noticed any strange behavior but need more testing). Fixed "Wipe Savedata", now it deletes folders. Added option in "Wipe Savedata" to delete saves from all users or current users. Changed all filesystem interaction to the IOSUHAX_FSA functions Now saves restored work ok even if files didn't exist previously If restoring a save when the 8000000X folder doesn't exist (game not run by user or deleted save in "System Settings") the save will work but will be undeletable using "System Settings", will need to use "Wipe Savedata" of Savemii. /usr/save/XXXXXXXX/XXXXXXXX/user/8000000X This change made sdcard writing slower (about 150KB/s) Show titleID, productCode and shortName in task selecting screen. Don't show option to backup/restore common save if common folder doen't exist in (NAND,USB)/SD. Began working (Not yet implemented) on being able to select the user (Of the ones in the WiiU console) to backup save from and restore save to. --- Makefile | 2 +- meta/meta.xml | 8 +- src/common/fs_defs.h | 1 - src/dynamic_libs/fs_defs.h | 5 + src/fs/fs_utils.c | 2 +- src/lib_easy.c | 104 ++++---- src/main.c | 202 ++++++++------ src/savemng.c | 526 ++++++++++++++++++++++++++----------- src/savemng.h | 8 +- 9 files changed, 565 insertions(+), 293 deletions(-) diff --git a/Makefile b/Makefile index 70b0024..a8517cc 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,7 @@ MAKEFLAGS += --no-print-directory #--------------------------------------------------------------------------------- # any extra libraries we wish to link with the project #--------------------------------------------------------------------------------- -LIBS := -lfat -liosuhax -lxml2 +LIBS := -lfat -liosuhax #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing diff --git a/meta/meta.xml b/meta/meta.xml index 50148f9..bdb68d4 100644 --- a/meta/meta.xml +++ b/meta/meta.xml @@ -1,9 +1,9 @@ - SaveMii + SaveMii Mod Ryuzaki_MrL original (GabyPCgeeK mod) - 1.2.0.m2 - 20170707000000 + 1.2.0.mod3 + 20170714000000 WiiU/vWii Save Manager WiiU/vWii Save Manager @@ -11,5 +11,5 @@ You need to run haxchi/iosuhax/mocha cfw first in order for this homebrew to wor This homebrew allows you to backup your Wii U and vWii savegames to the SD card and also restore them. Can also copy saves from NAND<->USB if title is installed to both. Up to 256 backups can be made per title. Backups are stored in sd:/wiiu/backups. tool -https://github.com/Ryuzaki-MrL/savemii/releases +https://github.com/GabyPCgeeK/savemii/releases diff --git a/src/common/fs_defs.h b/src/common/fs_defs.h index feda725..37feb3f 100644 --- a/src/common/fs_defs.h +++ b/src/common/fs_defs.h @@ -26,7 +26,6 @@ extern "C" { #define FS_SOURCETYPE_EXTERNAL 0 #define FS_SOURCETYPE_HFIO 1 -#define FS_SOURCETYPE_HFIO 1 #define FS_MOUNT_SOURCE_SIZE 0x300 #define FS_CLIENT_SIZE 0x1700 diff --git a/src/dynamic_libs/fs_defs.h b/src/dynamic_libs/fs_defs.h index 1b1bc41..9f7b02b 100644 --- a/src/dynamic_libs/fs_defs.h +++ b/src/dynamic_libs/fs_defs.h @@ -31,6 +31,11 @@ extern "C" { #define FS_CLIENT_SIZE 0x1700 #define FS_CMD_BLOCK_SIZE 0xA80 +#define FSA_STATUS_OK 0 +#define FSA_STATUS_ALREADY_EXISTS -22 +#define FSA_STATUS_NOT_FOUND -23 +#define FSA_STATUS_NOT_EMPTY -24 + typedef struct { uint32_t flag; diff --git a/src/fs/fs_utils.c b/src/fs/fs_utils.c index 613cec6..a748029 100644 --- a/src/fs/fs_utils.c +++ b/src/fs/fs_utils.c @@ -173,7 +173,7 @@ int CreateSubfolder(const char * fullpath) if(!result) return 0; - if (mkdir(dirnoslash, 0777) == -1) + if (mkdir(dirnoslash, 0x777) == -1) { return 0; } diff --git a/src/lib_easy.c b/src/lib_easy.c index ab444af..00226b6 100644 --- a/src/lib_easy.c +++ b/src/lib_easy.c @@ -7,33 +7,33 @@ int screen_buf0_size = 0; int screen_buf1_size = 0; void flipBuffers() { - // Flush the cache - DCFlushRange(screenBuffer, screen_buf0_size); - DCFlushRange((screenBuffer + screen_buf0_size), screen_buf1_size); - // Flip buffers - OSScreenFlipBuffersEx(0); - OSScreenFlipBuffersEx(1); + // Flush the cache + DCFlushRange(screenBuffer, screen_buf0_size); + DCFlushRange((screenBuffer + screen_buf0_size), screen_buf1_size); + // Flip buffers + OSScreenFlipBuffersEx(0); + OSScreenFlipBuffersEx(1); } void ucls() { - for(int i=0;i<2;i++) { - OSScreenClearBufferEx(0, 0); - OSScreenClearBufferEx(1, 0); - flipBuffers(); - } + for(int i=0;i<2;i++) { + OSScreenClearBufferEx(0, 0); + OSScreenClearBufferEx(1, 0); + flipBuffers(); + } } void ScreenInit() { - //Init screen and screen buffers - OSScreenInit(); - screen_buf0_size = OSScreenGetBufferSizeEx(0); - screen_buf1_size = OSScreenGetBufferSizeEx(1); - screenBuffer = MEM1_alloc(screen_buf0_size + screen_buf1_size, 0x40); - OSScreenSetBufferEx(0, screenBuffer); - OSScreenSetBufferEx(1, (screenBuffer + screen_buf0_size)); - OSScreenEnableEx(0, 1); - OSScreenEnableEx(1, 1); - ucls(); //Clear screens + //Init screen and screen buffers + OSScreenInit(); + screen_buf0_size = OSScreenGetBufferSizeEx(0); + screen_buf1_size = OSScreenGetBufferSizeEx(1); + screenBuffer = MEM1_alloc(screen_buf0_size + screen_buf1_size, 0x40); + OSScreenSetBufferEx(0, screenBuffer); + OSScreenSetBufferEx(1, (screenBuffer + screen_buf0_size)); + OSScreenEnableEx(0, 1); + OSScreenEnableEx(1, 1); + ucls(); //Clear screens } void updatePressedButtons() { @@ -52,18 +52,18 @@ void updateReleasedButtons() { } bool stickPos(u8 stick, f32 value) { - switch(stick) { - case 0 : - return (value > 0) ? (vpad.lstick.x > value): (vpad.lstick.x < value); - case 1 : - return ((value > 0) ? (vpad.lstick.y > value): (vpad.lstick.y < value)); - case 2 : - return (value > 0) ? (vpad.rstick.x > value): (vpad.rstick.x < value); - case 3 : - return (value > 0) ? (vpad.rstick.y > value): (vpad.rstick.y < value); - default : - return 0; - } + switch(stick) { + case 0 : + return (value > 0) ? (vpad.lstick.x > value): (vpad.lstick.x < value); + case 1 : + return ((value > 0) ? (vpad.lstick.y > value): (vpad.lstick.y < value)); + case 2 : + return (value > 0) ? (vpad.rstick.x > value): (vpad.rstick.x < value); + case 3 : + return (value > 0) ? (vpad.rstick.y > value): (vpad.rstick.y < value); + default : + return 0; + } } int isPressed(int button) { @@ -79,27 +79,27 @@ int isReleased(int button) { } void uInit() { - //--Initialize every function pointer-- (byebye FindExport :D) - InitOSFunctionPointers(); - InitSocketFunctionPointers(); - InitACPFunctionPointers(); - InitAocFunctionPointers(); - InitAXFunctionPointers(); - InitCurlFunctionPointers(); - InitFSFunctionPointers(); - InitGX2FunctionPointers(); - InitPadScoreFunctionPointers(); - InitSysFunctionPointers(); - InitSysHIDFunctionPointers(); - InitVPadFunctionPointers(); + //--Initialize every function pointer-- (byebye FindExport :D) + InitOSFunctionPointers(); + InitSocketFunctionPointers(); + InitACPFunctionPointers(); + InitAocFunctionPointers(); + InitAXFunctionPointers(); + InitCurlFunctionPointers(); + InitFSFunctionPointers(); + InitGX2FunctionPointers(); + InitPadScoreFunctionPointers(); + InitSysFunctionPointers(); + InitSysHIDFunctionPointers(); + InitVPadFunctionPointers(); - memoryInitialize(); //You probably shouldn't care about this for now :P - VPADInit(); //Init GamePad input library (needed for getting gamepad input) - ScreenInit(); //Init OSScreen (all the complex stuff is in easyfunctions.h :P ) + memoryInitialize(); //You probably shouldn't care about this for now :P + VPADInit(); //Init GamePad input library (needed for getting gamepad input) + ScreenInit(); //Init OSScreen (all the complex stuff is in easyfunctions.h :P ) } void uDeInit() { - MEM1_free(screenBuffer); - screenBuffer = NULL; - memoryRelease(); + MEM1_free(screenBuffer); + screenBuffer = NULL; + memoryRelease(); } diff --git a/src/main.c b/src/main.c index b46e1fb..1c097d0 100644 --- a/src/main.c +++ b/src/main.c @@ -12,6 +12,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 2 #define VERSION_MICRO 0 +#define VERSION_MOD "mod3" u8 slot = 0; bool allusers = 0, common = 1; @@ -30,7 +31,7 @@ static int mcp_hook_fd = -1; int MCPHookOpen() { //take over mcp thread mcp_hook_fd = MCP_Open(); - if(mcp_hook_fd < 0) return -1; + if (mcp_hook_fd < 0) return -1; IOS_IoctlAsync(mcp_hook_fd, 0x62, (void*)0, 0, (void*)0, 0, someFunc, (void*)0); //let wupserver start up sleep(1); @@ -43,7 +44,7 @@ int MCPHookOpen() { } void MCPHookClose() { - if(mcp_hook_fd < 0) return; + if (mcp_hook_fd < 0) return; //close down wupserver, return control to mcp IOSUHAX_Close(); //wait for mcp to return @@ -76,7 +77,7 @@ int titleSort(const void *c1, const void *c2) } } -Title* loadWiiUTitles(int run) { +Title* loadWiiUTitles(int run, int fsaFd) { static char *tList; static int receivedCount; // Source: haxchi installer @@ -99,44 +100,35 @@ Title* loadWiiUTitles(int run) { } for (int i = 0; i < receivedCount; 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); char path[255]; memset(path, 0, 255); - if (memcmp(element+0x56,"odd",4)==0) strcpy(path, "storage_odd:/meta/meta.xml"); - else sprintf(path, "storage_%s:/usr/title/%08x/%08x/meta/meta.xml", element+0x56, highID, lowID); + 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); + + int ret = IOSUHAX_FSA_OpenFile(fsaFd, path, "rb", &srcFd); + if (ret >= 0) { + fileStat_s fStat; + IOSUHAX_FSA_StatFile(fsaFd, srcFd, &fStat); + size_t xmlSize = fStat.size; - FILE* xmlFile = fopen(path, "rb"); - if (xmlFile) { - fseek(xmlFile, 0, SEEK_END); - size_t xmlSize = ftell(xmlFile); - fseek(xmlFile, 0, SEEK_SET); char* xmlBuf = malloc(xmlSize+1); if (xmlBuf) { memset(xmlBuf, 0, xmlSize+1); - fread(xmlBuf, 1, xmlSize, xmlFile); - fclose(xmlFile); + IOSUHAX_FSA_ReadFile(fsaFd, xmlBuf, 0x01, xmlSize, srcFd, 0); + IOSUHAX_FSA_CloseFile(fsaFd, srcFd); - 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) || - (xmlNodeGetContent(cur_node) == NULL) || - (!strlen((char*)xmlNodeGetContent(cur_node))) - ) continue; + char *cptr = strchr(strstr(xmlBuf, "product_code"), '>') + 7; + strncpy(titles[titleswiiu].productCode, cptr, strcspn(cptr, "<")); - if (!memcmp(cur_node->name, "shortname_en", 13)) - strcpy(titles[titleswiiu].shortName, (char*)xmlNodeGetContent(cur_node)); - if (!memcmp(cur_node->name, "product_code", 13)) - strcpy(titles[titleswiiu].productCode, (char*)(xmlNodeGetContent(cur_node)+6)); - } + cptr = strchr(strstr(xmlBuf, "shortname_en"), '>') + 1; + memset(titles[titleswiiu].shortName, 0, sizeof(titles[titleswiiu].shortName)); + strncpy(titles[titleswiiu].shortName, cptr, strcspn(cptr, "<")); - xmlFreeDoc(tmp); free(xmlBuf); } } @@ -170,18 +162,20 @@ Title* loadWiiUTitles(int run) { } -Title* loadWiiTitles() { +Title* loadWiiTitles(int fsaFd) { + int dirH; - struct dirent* dirent = NULL; - DIR* dir = NULL; + if (IOSUHAX_FSA_OpenDir(fsaFd, "/vol/storage_slccmpt01/title/00010000", &dirH) < 0) return -1; - dir = opendir("slccmpt01:/title/00010000"); - if (dir == NULL) return NULL; + while (1) { + directoryEntry_s data; + int ret = IOSUHAX_FSA_ReadDir(fsaFd, dirH, &data); + if (ret != 0) + break; - while ((dirent = readdir(dir)) != 0) { - if(strcmp(dirent->d_name, "..")==0 || strcmp(dirent->d_name, ".")==0) continue; + if (strcmp(data.name, "..") == 0 || strcmp(data.name, ".") == 0) continue; titlesvwii++; - } rewinddir(dir); + } IOSUHAX_FSA_RewindDir(fsaFd, dirH); Title* titles = malloc(titlesvwii*sizeof(Title)); if (!titles) { @@ -190,28 +184,52 @@ Title* loadWiiTitles() { } int i = 0; - while ((dirent = readdir(dir)) != 0) { + while (1) { + directoryEntry_s data; + int ret = IOSUHAX_FSA_ReadDir(fsaFd, dirH, &data); + if (ret != 0) + break; - if(strcmp(dirent->d_name, "..")==0 || strcmp(dirent->d_name, ".")==0) continue; + if (strcmp(data.name, "..") == 0 || strcmp(data.name, ".") == 0) continue; + int srcFd = -1; 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); + sprintf(path, "/vol/storage_slccmpt01/title/00010000/%s/data/banner.bin", data.name); + ret = IOSUHAX_FSA_OpenFile(fsaFd, path, "rb", &srcFd); + if (ret >= 0) { + IOSUHAX_FSA_SetFilePos(fsaFd, srcFd, 0x20); u16* bnrBuf = (u16*)malloc(0x40); if (bnrBuf) { - fread(bnrBuf, 0x02, 0x20, bnrFile); - fclose(bnrFile); + IOSUHAX_FSA_ReadFile(fsaFd, bnrBuf, 0x02, 0x20, srcFd, 0); + IOSUHAX_FSA_CloseFile(fsaFd, srcFd); + + int uni = 0; for (int j = 0, k = 0; j < 0x20; j++) { - titles[i].shortName[k++] = (char)bnrBuf[j]; + if (bnrBuf[j] > 127) { + titles[i].shortName[k++] = '?'; + uni++; + } else { + titles[i].shortName[k++] = (char)bnrBuf[j]; + if ((char)bnrBuf[j] == 0) + uni++; + } } free(bnrBuf); + if (uni == 0x20) + sprintf(titles[i].shortName, "00010000%s (No ASCII char in title)", data.name); } + } else { + sprintf(titles[i].shortName, "00010000%s (No banner.bin)", data.name); } titles[i].highID = 0x00010000; - titles[i].lowID = strtoul(dirent->d_name, NULL, 16); + titles[i].lowID = strtoul(data.name, NULL, 16); + + titles[i].listID = i; + sprintf(titles[i].productCode, "?"); + titles[i].isTitleOnUSB = false; + titles[i].isTitleDupe = false; + titles[i].dupeID = 0; i++; OSScreenClearBufferEx(0, 0); @@ -219,10 +237,9 @@ Title* loadWiiTitles() { console_print_pos(0, 1, "Loaded %i Wii titles.", i); OSScreenFlipBuffersEx(0); OSScreenFlipBuffersEx(1); - } - closedir(dir); + IOSUHAX_FSA_CloseDir(fsaFd, dirH); return titles; } @@ -234,8 +251,8 @@ void unloadTitles(Title* titles) { /* Entry point */ int Menu_Main(void) { - mount_sd_fat("sd"); - loadWiiUTitles(0); + //mount_sd_fat("sd"); + loadWiiUTitles(0, -1); int res = IOSUHAX_Open(NULL); if (res < 0) { @@ -247,25 +264,28 @@ int Menu_Main(void) { return EXIT_SUCCESS; } - fatInitDefault(); + //fatInitDefault(); int fsaFd = IOSUHAX_FSA_Open(); if (fsaFd < 0) { promptError("IOSUHAX_FSA_Open failed."); - unmount_sd_fat("sd"); + //unmount_sd_fat("sd"); if (mcp_hook_fd >= 0) MCPHookClose(); else IOSUHAX_Close(); return EXIT_SUCCESS; } + setFSAFD(fsaFd); + IOSUHAX_FSA_Mount(fsaFd, "/dev/sdcard01", "/vol/storage_sdcard", 2, (void*)0, 0); + //mount_fs("sd", fsaFd, NULL, "/vol/storage_sdcard"); 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"); mount_fs("storage_odd", fsaFd, "/dev/odd03", "/vol/storage_odd_content"); ucls(); - Title* wiiutitles = loadWiiUTitles(1); - Title* wiititles = loadWiiTitles(); + Title* wiiutitles = loadWiiUTitles(1, fsaFd); + Title* wiititles = loadWiiTitles(fsaFd); int* versionList = (int*)malloc(0x100*sizeof(int)); while(1) { @@ -273,7 +293,7 @@ int Menu_Main(void) { OSScreenClearBufferEx(0, 0); OSScreenClearBufferEx(1, 0); - console_print_pos(0, 0, "SaveMii v%u.%u.%u.%s", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO, "mod2"); + console_print_pos(0, 0, "SaveMii v%u.%u.%u.%s", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO, VERSION_MOD); console_print_pos(0, 1, "--------------------------------------------------"); Title* titles = mode ? wiititles : wiiutitles; @@ -292,25 +312,27 @@ 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" : "NAND", titles[i+scroll].isTitleDupe ? "[D]" : ""); + 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]" : ""); else console_print_pos(0, i+2, " %08lx%08lx", titles[i+scroll].highID, titles[i+scroll].lowID); } console_print_pos(0, 2 + cursor, "->"); } break; case 2: { // Select Task entrycount = 3 + 2*(mode==0) + 1*((mode==0) && (titles[targ].isTitleDupe)); - console_print_pos(0, 2, " Backup savedata"); - console_print_pos(0, 3, " Restore savedata"); - console_print_pos(0, 4, " Wipe savedata"); + console_print_pos(0, 2, " [%08X%08X]", titles[targ].highID, titles[targ].lowID); + console_print_pos(0, 3, " [%s] %s", titles[targ].productCode, titles[targ].shortName); + console_print_pos(0, 2+3, " Backup savedata"); + console_print_pos(0, 3+3, " Restore savedata"); + console_print_pos(0, 4+3, " Wipe savedata"); if (mode==0) { - console_print_pos(0, 5, " Import from loadiine"); - console_print_pos(0, 6, " Export to loadiine"); + console_print_pos(0, 5+3, " Import from loadiine"); + console_print_pos(0, 6+3, " Export to loadiine"); if (titles[targ].isTitleDupe) { - console_print_pos(0, 7, " Copy Savedata to Title in %s", titles[targ].isTitleOnUSB ? "NAND" : "USB"); + console_print_pos(0, 7+3, " Copy Savedata to Title in %s", titles[targ].isTitleOnUSB ? "NAND" : "USB"); } } - console_print_pos(0, 2 + cursor, "->"); + console_print_pos(0, 2+3 + cursor, "->"); } break; case 3: { // Select Options entrycount = 1 + 2*(mode==0); @@ -323,8 +345,14 @@ int Menu_Main(void) { char usrPath[16]; getUserID(usrPath); console_print_pos(0, 6, " < %s > %s", (allusers&&((task<3) || task==5)) ? "all users" : "this user", (allusers&&((task<3) || task==5)) ? "" : usrPath); - console_print_pos(0, 8, "Include 'common' save?"); - console_print_pos(0, 9, " < %s >", common ? "yes" : "no "); + if (hasCommonSave(&titles[targ], (task == 0 ? false : true), (task < 3 ? false : true), slot, versionList ? versionList[slot] : 0)) { + console_print_pos(0, 8, "Include 'common' save?"); + console_print_pos(0, 9, " < %s >", common ? "yes" : "no "); + } else { + common = false; + console_print_pos(0, 8, "No 'common' save found."); + entrycount--; + } console_print_pos(0, 3 + cursor*3, "->"); } } break; @@ -382,30 +410,43 @@ int Menu_Main(void) { sorta *= -1; qsort(wiiutitles, titleswiiu, sizeof(Title), titleSort); } + + if (isPressed(VPAD_BUTTON_X) && (menu == 1) && (mode == 0)) { + /*targ = cursor+scroll; + if (titles[targ].highID == 0 || titles[targ].lowID == 0) continue; + SYSCheckTitleExists((uint64_t) titles[targ].highID << 32 | titles[targ].lowID); + + if (SYSCheckTitleExists((uint64_t) titles[targ].highID << 32 | titles[targ].lowID)) { + SYSLaunchTitle((uint64_t) titles[targ].highID << 32 | titles[targ].lowID); + break; + }*/ + } + if (isPressed(VPAD_BUTTON_A)) { ucls(); - if (menu<3) { - if (menu==0) { + if (menu < 3) { + if (menu == 0) { mode = cursor; - if (mode==0 && (!wiiutitles || !titleswiiu)) { + if (mode == 0 && (!wiiutitles || !titleswiiu)) { promptError("No Wii U titles found."); continue; } - if (mode==1 && (!wiititles || !titlesvwii)) { + if (mode == 1 && (!wiititles || !titlesvwii)) { promptError("No vWii saves found."); continue; } } - if (menu==1) { + if (menu == 1) { targ = cursor+scroll; cursorb = cursor; scrollb = scroll; if (titles[targ].highID==0 || titles[targ].lowID==0) continue; } - if (menu==2) { + if (menu == 2) { task = cursor; - if (task==2) { - wipeSavedata(&titles[targ], allusers, common); + if (task == 2) { + allusers = promptConfirm("Delete save from all users?"); + wipeSavedata(&titles[targ], allusers, hasCommonSave(&titles[targ], false, false, 0, 0)); continue; } if (task > 2) { @@ -421,20 +462,22 @@ int Menu_Main(void) { switch(task) { case 0: backupSavedata(&titles[targ], slot, allusers, common); break; case 1: restoreSavedata(&titles[targ], slot, allusers, common); break; - case 2: wipeSavedata(&titles[targ], 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; 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; } } - } else if (isPressed(VPAD_BUTTON_B) && menu>0) { + } else if (isPressed(VPAD_BUTTON_B) && menu > 0) { ucls(); menu--; cursor = scroll = 0; - if (menu==1) { - cursor = cursorb; - scroll = scrollb; - } + if (menu == 1) { + cursor = cursorb; + scroll = scrollb; + } } if (isPressed(VPAD_BUTTON_HOME)) break; @@ -451,6 +494,7 @@ int Menu_Main(void) { IOSUHAX_usb_disc_interface.shutdown(); unmount_fs("slccmpt01"); + //unmount_fs("sd"); unmount_fs("storage_mlc"); unmount_fs("storage_usb"); unmount_fs("storage_odd"); diff --git a/src/savemng.c b/src/savemng.c index d399ca6..bf03d6f 100644 --- a/src/savemng.c +++ b/src/savemng.c @@ -1,6 +1,83 @@ #include "savemng.h" -#define BUFFER_SIZE 0x80000 +#define BUFFER_SIZE 0x8020//0x2820 +#define BUFFER_SIZE_STEPS 0x20 + +int fsaFd = -1; +char * p1; + + +void setFSAFD(int fd) { + fsaFd = fd; +} + +void replace_str(char *str, char *orig, char *rep, int start, char *out) { + static char temp[PATH_SIZE]; + static char buffer[PATH_SIZE]; + char *p; + + strcpy(temp, str + start); + + if (!(p = strstr(temp, orig))) // Is 'orig' even in 'temp'? + return temp; + + strncpy(buffer, temp, p-temp); // Copy characters from 'temp' start to 'orig' str + buffer[p-temp] = '\0'; + + sprintf(buffer + (p - temp), "%s%s", rep, p + strlen(orig)); + sprintf(out, "%s", buffer); +} + +void convert_to_string(uint16_t *wide, char *buf) { + // convert wide-char to normal char + while (*buf++ = (char)*wide++); +} + +int16_t FSAR(int result) { + return (int16_t) (result & 0xFFFF); +} + +int checkEntry(const char * fPath) { + fileStat_s fStat; + int ret = FSAR(IOSUHAX_FSA_GetStat(fsaFd, fPath, &fStat)); + + if (ret == FSA_STATUS_NOT_FOUND) return 0; + else if (ret < 0) return -1; + + if (fStat.flag & DIR_ENTRY_IS_DIRECTORY) return 2; + else return 1; +} + +int createFolder(const char * fPath) { //Adapted from mkdir_p made by JonathonReinhart + const size_t len = strlen(fPath); + char _path[FS_MAX_FULLPATH_SIZE]; + char *p; + int ret, found = 0; + + if (len > sizeof(_path)-1) { + return -1; + } + strcpy(_path, fPath); + + for (p = _path + 1; *p; p++) { + if (*p == '/') { + found++; + if (found > 2) { + *p = '\0'; + if (checkEntry(_path) == 0) { + if ((ret = FSAR(IOSUHAX_FSA_MakeDir(fsaFd, _path, 0x666))) < 0) return -1; + } + *p = '/'; + } + } + } + + if (checkEntry(_path) == 0) { + if ((ret = FSAR(IOSUHAX_FSA_MakeDir(fsaFd, _path, 0x666))) < 0) return -1; + } + + return 0; +} void console_print_pos(int x, int y, const char* format, ...) { // Source: ftpiiu @@ -22,6 +99,23 @@ 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) { ucls(); const char* msg = "(A) Confirm - (B) Cancel"; @@ -42,111 +136,173 @@ bool promptConfirm(const char* question) { return ret; } -void promptError(const char* message) { +void promptError(const char* message, ...) { ucls(); - while(1) { - OSScreenClearBufferEx(0, 0); - OSScreenClearBufferEx(1, 0); - console_print_pos(25 - (strlen(message)>>1), 9, message); - OSScreenFlipBuffersEx(0); - OSScreenFlipBuffersEx(1); + va_list va; + va_start(va, message); + OSScreenClearBufferEx(0, 0); + OSScreenClearBufferEx(1, 0); + console_print_pos_va(25 - (strlen(message)>>1), 9, message, va); + OSScreenFlipBuffersEx(0); + OSScreenFlipBuffersEx(1); + va_end(va); + while(1) { updatePressedButtons(); if (isPressed(0xFFFF)) break; } } -int DumpFile(char* pPath, const char* output_path) { // Source: ft2sd +void getAccounts() { + /* get persistent ID - thanks to Maschell */ + unsigned int nn_act_handle; + unsigned long (*GetPersistentIdEx)(unsigned char); + bool (*IsSlotOccupied)(unsigned char); + int (*GetSlotNo)(void); + int (*GetNumOfAccounts)(void); + int (*GetMiiNameEx)(uint16_t*, unsigned char); + void (*nn_Initialize)(void); + void (*nn_Finalize)(void); + OSDynLoad_Acquire("nn_act.rpl", &nn_act_handle); + OSDynLoad_FindExport(nn_act_handle, 0, "GetPersistentIdEx__Q2_2nn3actFUc", &GetPersistentIdEx); + OSDynLoad_FindExport(nn_act_handle, 0, "IsSlotOccupied__Q2_2nn3actFUc", &IsSlotOccupied); + OSDynLoad_FindExport(nn_act_handle, 0, "GetSlotNo__Q2_2nn3actFv", &GetSlotNo); + OSDynLoad_FindExport(nn_act_handle, 0, "GetNumOfAccounts__Q2_2nn3actFv", &GetNumOfAccounts); + OSDynLoad_FindExport(nn_act_handle, 0, "GetMiiNameEx__Q2_2nn3actFPwUc", &GetMiiNameEx); + OSDynLoad_FindExport(nn_act_handle, 0, "Initialize__Q2_2nn3actFv", &nn_Initialize); + OSDynLoad_FindExport(nn_act_handle, 0, "Finalize__Q2_2nn3actFv", &nn_Finalize); - unsigned char* dataBuf = (unsigned char*)memalign(0x40, BUFFER_SIZE); - if (!dataBuf) { - promptError("Out of memory."); - return -1; - } - - FILE* pReadFile = fopen(pPath, "rb"); - if (!pReadFile) { - promptError("Failed to open file."); - free(dataBuf); - return -2; - } - - FILE* pWriteFile = fopen(output_path, "wb"); - if (!pWriteFile) { - promptError("Failed to create file."); - fclose(pReadFile); - free(dataBuf); - return -3; - } - - u32 rsize; - while ((rsize = fread(dataBuf, 0x1, BUFFER_SIZE, pReadFile)) > 0) { - u32 wsize = fwrite(dataBuf, 0x01, rsize, pWriteFile); - if (wsize!=rsize) { - promptError("Failed to write to file."); - fclose(pWriteFile); - fclose(pReadFile); - free(dataBuf); - return -4; - } - } - - fclose(pWriteFile); - fclose(pReadFile); - free(dataBuf); - - return 0; + nn_Initialize(); // To be sure that it is really Initialized + int i = 0, acc = GetNumOfAccounts(); + char acclist[13][9], nlist[13][11]; + uint16_t out[11]; + while ((acc > 0) && (i <= 12)) { + if (IsSlotOccupied(i)) { + unsigned int persistentID = GetPersistentIdEx(i); + sprintf(acclist[i], "%08X", persistentID); + GetMiiNameEx(out, i); + convert_to_string(out, nlist[i]); + promptError("%d/%s/%s", i, acclist[i], nlist[i]); + acc--; + } + i++; + } + nn_Finalize(); //must be called an equal number of times to nn_Initialize } -int DumpDir(char* pPath, const char* target_path) { // Source: ft2sd +int DumpFile(char* pPath, const char* oPath) { + int srcFd = -1, destFd = -1; + int ret = 0; + int buf_size = BUFFER_SIZE; + uint8_t * pBuffer; - struct dirent* dirent = NULL; - DIR* dir = NULL; + do{ + buf_size -= BUFFER_SIZE_STEPS; + if (buf_size < 0) { + promptError("Error allocating Buffer."); + return; + } + pBuffer = (uint8_t *)memalign(0x40, buf_size); + if (pBuffer) memset(pBuffer, 0x00, buf_size); + }while(!pBuffer); - dir = opendir(pPath); - if (dir == NULL) return -1; + ret = IOSUHAX_FSA_OpenFile(fsaFd, pPath, "rb", &srcFd); + if (ret >= 0) { + fileStat_s fStat; + IOSUHAX_FSA_StatFile(fsaFd, srcFd, &fStat); + if ((ret = IOSUHAX_FSA_OpenFile(fsaFd, oPath, "wb", &destFd)) >= 0) { + int result, sizew = 0, sizef = fStat.size; + int fwrite = 0; + u32 passedMs = 1; + u64 startTime = OSGetTime(); - CreateSubfolder(target_path); + while ((result = IOSUHAX_FSA_ReadFile(fsaFd, pBuffer, 0x01, buf_size, srcFd, 0)) > 0) { + if ((fwrite = IOSUHAX_FSA_WriteFile(fsaFd, pBuffer, 0x01, result, destFd, 0)) < 0) { + promptError("Write %d,%s", fwrite, oPath); + IOSUHAX_FSA_CloseFile(fsaFd, destFd); + IOSUHAX_FSA_CloseFile(fsaFd, srcFd); + free(pBuffer); + return -1; + } + sizew += fwrite; + passedMs = (OSGetTime() - startTime) * 4000ULL / BUS_SPEED; + if(passedMs == 0) + passedMs = 1; - while ((dirent = readdir(dir)) != 0) { + OSScreenClearBufferEx(0, 0); + OSScreenClearBufferEx(1, 0); + console_print_pos(0, 0, "Copying file: %s", p1); + console_print_pos(0, 1, "From: %s", pPath); + console_print_pos(0, 2, "To: %s", oPath); + console_print_pos(0, 4, "Bytes Copied: %d of %d (%i kB/s)", sizew, sizef, (u32)(((u64)sizew * 1000) / ((u64)1024 * passedMs))); + OSScreenFlipBuffersEx(0); + OSScreenFlipBuffersEx(1); + } + } else { + promptError("Open File W %d,%s", ret, oPath); + IOSUHAX_FSA_CloseFile(fsaFd, srcFd); + free(pBuffer); + return -1; + } + IOSUHAX_FSA_CloseFile(fsaFd, destFd); + IOSUHAX_FSA_CloseFile(fsaFd, srcFd); + IOSUHAX_FSA_ChangeMode(fsaFd, oPath, 0x666); + free(pBuffer); + } else { + promptError("Open File R %d,%s", ret, pPath); + free(pBuffer); + return -1; + } + + return 0; +} + +int DumpDir(char* pPath, const char* tPath) { // Source: ft2sd + int dirH; + + if (IOSUHAX_FSA_OpenDir(fsaFd, pPath, &dirH) < 0) return -1; + IOSUHAX_FSA_MakeDir(fsaFd, tPath, 0x666); + + while (1) { + directoryEntry_s data; + int ret = IOSUHAX_FSA_ReadDir(fsaFd, dirH, &data); + if (ret != 0) + break; OSScreenClearBufferEx(0, 0); OSScreenClearBufferEx(1, 0); - if (strcmp(dirent->d_name, "..") == 0 || strcmp(dirent->d_name, ".") == 0) continue; + if (strcmp(data.name, "..") == 0 || strcmp(data.name, ".") == 0) continue; int len = strlen(pPath); - snprintf(pPath + len, FS_MAX_FULLPATH_SIZE - len, "/%s", dirent->d_name); - - if (dirent->d_type & DT_DIR) { + snprintf(pPath + len, FS_MAX_FULLPATH_SIZE - len, "/%s", data.name); + if (data.stat.flag & DIR_ENTRY_IS_DIRECTORY) { char* targetPath = (char*)malloc(FS_MAX_FULLPATH_SIZE); - snprintf(targetPath, FS_MAX_FULLPATH_SIZE, "%s/%s", target_path, dirent->d_name); + snprintf(targetPath, FS_MAX_FULLPATH_SIZE, "%s/%s", tPath, data.name); - CreateSubfolder(targetPath); - if (DumpDir(pPath, targetPath)!=0) { - closedir(dir); + IOSUHAX_FSA_MakeDir(fsaFd, targetPath, 0x666); + if (DumpDir(pPath, targetPath) != 0) { + IOSUHAX_FSA_CloseDir(fsaFd, dirH); return -2; } free(targetPath); - } else { - char* targetPath = (char*)malloc(FS_MAX_FULLPATH_SIZE); - snprintf(targetPath, FS_MAX_FULLPATH_SIZE, "%s/%s", target_path, dirent->d_name); + snprintf(targetPath, FS_MAX_FULLPATH_SIZE, "%s/%s", tPath, data.name); - console_print_pos(0, 0, "Copying file %s", dirent->d_name); + p1 = data.name; + console_print_pos(0, 0, "Copying file: %s", data.name); console_print_pos(0, 1, "From: %s", pPath); console_print_pos(0, 2, "To: %s", targetPath); - if (DumpFile(pPath, targetPath)!=0) { - closedir(dir); + if (DumpFile(pPath, targetPath) != 0) { + IOSUHAX_FSA_CloseDir(fsaFd, dirH); return -3; } free(targetPath); - } OSScreenFlipBuffersEx(0); @@ -156,53 +312,63 @@ int DumpDir(char* pPath, const char* target_path) { // Source: ft2sd } - closedir(dir); + /*if (strcspn(tPath, "_usb") > 0) { + IOSUHAX_FSA_FlushVolume(fsaFd, "/vol/storage_usb01"); + } else if (strcspn(tPath, "_mlc") > 0) { + IOSUHAX_FSA_FlushVolume(fsaFd, "/vol/storage_mlc01"); + }*/ + + IOSUHAX_FSA_CloseDir(fsaFd, dirH); return 0; } int DeleteDir(char* pPath) { + int dirH; - struct dirent* dirent = NULL; - DIR* dir = NULL; + if (IOSUHAX_FSA_OpenDir(fsaFd, pPath, &dirH) < 0) return -1; - dir = opendir(pPath); - if (dir == NULL) return -1; - - while ((dirent = readdir(dir)) != 0) { + while (1) { + directoryEntry_s data; + int ret = IOSUHAX_FSA_ReadDir(fsaFd, dirH, &data); + if (ret != 0) + break; OSScreenClearBufferEx(0, 0); OSScreenClearBufferEx(1, 0); - if (strcmp(dirent->d_name, "..") == 0 || strcmp(dirent->d_name, ".") == 0) continue; + if (strcmp(data.name, "..") == 0 || strcmp(data.name, ".") == 0) continue; int len = strlen(pPath); - snprintf(pPath + len, FS_MAX_FULLPATH_SIZE - len, "/%s", dirent->d_name); + snprintf(pPath + len, FS_MAX_FULLPATH_SIZE - len, "/%s", data.name); - if (dirent->d_type & DT_DIR) { + if (data.stat.flag & DIR_ENTRY_IS_DIRECTORY) { + char origPath[PATH_SIZE]; + sprintf(origPath, "%s", pPath); DeleteDir(pPath); + + OSScreenClearBufferEx(0, 0); + OSScreenClearBufferEx(1, 0); + console_print_pos(0, 0, "Deleting folder %s", data.name); + console_print_pos(0, 1, "From: %s", origPath); + if (IOSUHAX_FSA_Remove(fsaFd, origPath) != 0) promptError("Failed to delete folder."); } else { - console_print_pos(0, 0, "Deleting file %s", dirent->d_name); + console_print_pos(0, 0, "Deleting file %s", data.name); console_print_pos(0, 1, "From: %s", pPath); - if (remove(pPath)!=0) promptError("Failed to delete file."); + if (IOSUHAX_FSA_Remove(fsaFd, pPath) != 0) promptError("Failed to delete file."); } OSScreenFlipBuffersEx(0); OSScreenFlipBuffersEx(1); - pPath[len] = 0; - } - closedir(dir); - + IOSUHAX_FSA_CloseDir(fsaFd, dirH); return 0; - } void getUserID(char* out) { // Source: loadiine_gx2 - /* get persistent ID - thanks to Maschell */ unsigned int nn_act_handle; unsigned long (*GetPersistentIdEx)(unsigned char); @@ -226,90 +392,85 @@ void getUserID(char* out) { // Source: loadiine_gx2 } int getLoadiineGameSaveDir(char* out, const char* productCode) { + int dirH; - struct dirent* dirent = NULL; - DIR* dir = NULL; + if (IOSUHAX_FSA_OpenDir(fsaFd, "/vol/storage_sdcard/wiiu/saves", &dirH) < 0) return -1; - dir = opendir("sd:/wiiu/saves"); - if (dir == NULL) { - promptError("Failed to open directory."); - return -1; - } + while (1) { + directoryEntry_s data; + int ret = IOSUHAX_FSA_ReadDir(fsaFd, dirH, &data); + if (ret != 0) + break; - while ((dirent = readdir(dir)) != 0) { - - if ((dirent->d_type & DT_DIR) && (strstr(dirent->d_name, productCode)!=NULL)) { - sprintf(out, "sd:/wiiu/saves/%s", dirent->d_name); - closedir(dir); + if ((data.stat.flag & DIR_ENTRY_IS_DIRECTORY) && (strstr(data.name, productCode) != NULL)) { + sprintf(out, "/vol/storage_sdcard/wiiu/saves/%s", data.name); + IOSUHAX_FSA_CloseDir(fsaFd, dirH); return 0; } - } promptError("Loadiine game folder not found."); - closedir(dir); + IOSUHAX_FSA_CloseDir(fsaFd, dirH); return -2; - } int getLoadiineSaveVersionList(int* out, const char* gamePath) { + int dirH; - struct dirent* dirent = NULL; - DIR* dir = NULL; - - dir = opendir(gamePath); - if (dir == NULL) { - promptError("Loadiine game folder not found."); - return -1; - } + if (IOSUHAX_FSA_OpenDir(fsaFd, gamePath, &dirH) < 0) { + promptError("Loadiine game folder not found."); + return -1; + } int i = 0; - while ((i < 255) && ((dirent = readdir(dir)) != 0)) { + while (i < 255) { + directoryEntry_s data; + int ret = IOSUHAX_FSA_ReadDir(fsaFd, dirH, &data); + if (ret != 0) + break; - if ((dirent->d_type & DT_DIR) && (strchr(dirent->d_name, 'v')!=NULL)) { - out[++i] = strtol((dirent->d_name)+1, NULL, 10); + if ((data.stat.flag & DIR_ENTRY_IS_DIRECTORY) && (strchr(data.name, 'v') != NULL)) { + out[++i] = strtol((data.name)+1, NULL, 10); } } - closedir(dir); + IOSUHAX_FSA_CloseDir(fsaFd, dirH); return 0; - } int getLoadiineUserDir(char* out, const char* fullSavePath, const char* userID) { + int dirH; - struct dirent* dirent = NULL; - DIR* dir = NULL; - - dir = opendir(fullSavePath); - if (dir == NULL) { + if (IOSUHAX_FSA_OpenDir(fsaFd, fullSavePath, &dirH) < 0) { promptError("Failed to open directory."); return -1; } - while ((dirent = readdir(dir)) != 0) { + while (1) { + directoryEntry_s data; + int ret = IOSUHAX_FSA_ReadDir(fsaFd, dirH, &data); + if (ret != 0) + break; - if ((dirent->d_type & DT_DIR) && (strstr(dirent->d_name, userID))) { - sprintf(out, "%s/%s", fullSavePath, dirent->d_name); - closedir(dir); + if ((data.stat.flag & DIR_ENTRY_IS_DIRECTORY) && (strstr(data.name, userID))) { + sprintf(out, "%s/%s", fullSavePath, data.name); + IOSUHAX_FSA_CloseDir(fsaFd, dirH); return 0; } - } sprintf(out, "%s/u", fullSavePath); - closedir(dir); + IOSUHAX_FSA_CloseDir(fsaFd, dirH); return 0; - } bool isSlotEmpty(u32 highID, u32 lowID, u8 slot) { char path[PATH_SIZE]; - sprintf(path, "sd:/wiiu/backups/%08x%08x/%u", highID, lowID, slot); - DIR* dir = opendir(path); - if (dir==NULL) return 1; - else return closedir(dir); + sprintf(path, "/vol/storage_sdcard/wiiu/backups/%08x%08x/%u", highID, lowID, slot); + int ret = checkEntry(path); + if (ret <= 0) return 1; + else return 0; } int getEmptySlot(u32 highID, u32 lowID) { @@ -319,6 +480,33 @@ int getEmptySlot(u32 highID, u32 lowID) { return -1; } +bool hasCommonSave(Title* title, bool inSD, bool iine, u8 slot, int version) { + u32 highID = title->highID, lowID = title->lowID; + bool isUSB = title->isTitleOnUSB, isWii = (highID==0x00010000); + if (isWii) return false; + + char srcPath[PATH_SIZE]; + const char* path = (isUSB ? "/vol/storage_usb01/usr/save" : "/vol/storage_mlc01/usr/save"); + if (!inSD) + sprintf(srcPath, "%s/%08x/%08x/%s/common", path, highID, lowID, "user"); + else { + if (!iine) + sprintf(srcPath, "/vol/storage_sdcard/wiiu/backups/%08x%08x/%u/common", highID, lowID, slot); + else { + if (getLoadiineGameSaveDir(srcPath, title->productCode) != 0) return false; + if (version) sprintf(srcPath + strlen(srcPath), "/v%u", version); + char usrPath[16]; + getUserID(usrPath); + u32 srcOffset = strlen(srcPath); + getLoadiineUserDir(srcPath, srcPath, usrPath); + strcpy(srcPath + srcOffset, "/c\0"); + } + } + if (checkEntry(srcPath) == 2) + return true; + return false; +} + void copySavedata(Title* title, Title* titleb, bool allusers, bool common) { u32 highID = title->highID, lowID = title->lowID; bool isUSB = title->isTitleOnUSB; @@ -334,8 +522,8 @@ void copySavedata(Title* title, Title* titleb, bool allusers, bool common) { char srcPath[PATH_SIZE]; char dstPath[PATH_SIZE]; - const char* path = (isUSB ? "storage_usb:/usr/save" : "storage_mlc:/usr/save"); - const char* pathb = (isUSBb ? "storage_usb:/usr/save" : "storage_mlc:/usr/save"); + const char* path = (isUSB ? "/vol/storage_usb01/usr/save" : "/vol/storage_mlc01/usr/save"); + 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"); if (!allusers) { @@ -346,14 +534,18 @@ void copySavedata(Title* title, Title* titleb, bool allusers, bool common) { if (common) { strcpy(srcPath + srcOffset, "/common"); strcpy(dstPath + dstOffset, "/common"); - if (DumpDir(srcPath, dstPath)!=0) promptError("Common save not found."); + if (DumpDir(srcPath, dstPath) != 0) promptError("Common save not found."); } sprintf(srcPath + srcOffset, "/%s", usrPath); sprintf(dstPath + dstOffset, "/%s", usrPath); } - if (DumpDir(srcPath, dstPath)!=0) promptError("Copy failed."); - promptError(srcPath); - promptError(dstPath); + if (DumpDir(srcPath, dstPath) != 0) promptError("Copy failed."); + + if (strcspn(dstPath, "_usb") > 0) { + IOSUHAX_FSA_FlushVolume(fsaFd, "/vol/storage_usb01"); + } else if (strcspn(dstPath, "_mlc") > 0) { + IOSUHAX_FSA_FlushVolume(fsaFd, "/vol/storage_mlc01"); + } } void backupSavedata(Title* title, u8 slot, bool allusers, bool common) { @@ -363,9 +555,13 @@ void backupSavedata(Title* title, u8 slot, bool allusers, bool common) { bool isUSB = title->isTitleOnUSB, isWii = (highID==0x00010000); char srcPath[PATH_SIZE]; char dstPath[PATH_SIZE]; - const char* path = (isWii ? "slccmpt01:/title" : (isUSB ? "storage_usb:/usr/save" : "storage_mlc:/usr/save")); + 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"); - sprintf(dstPath, "sd:/wiiu/backups/%08x%08x/%u", highID, lowID, slot); + 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); if (!allusers && !isWii) { char usrPath[16]; getUserID(usrPath); @@ -374,13 +570,22 @@ void backupSavedata(Title* title, u8 slot, bool allusers, bool common) { if (common) { strcpy(srcPath + srcOffset, "/common"); strcpy(dstPath + dstOffset, "/common"); - if (DumpDir(srcPath, dstPath)!=0) promptError("Common save not found."); + if (DumpDir(srcPath, dstPath) != 0) promptError("Common save not found."); } sprintf(srcPath + srcOffset, "/%s", usrPath); sprintf(dstPath + dstOffset, "/%s", usrPath); + if (checkEntry(srcPath) == 0) { + promptError("No save found for this user."); + return; + } } - if (DumpDir(srcPath, dstPath)!=0) promptError("Backup failed. DO NOT restore from this slot."); + if (DumpDir(srcPath, dstPath) != 0) promptError("Backup failed. DO NOT restore from this slot."); + if (strcspn(dstPath, "_usb") > 0) { + IOSUHAX_FSA_FlushVolume(fsaFd, "/vol/storage_usb01"); + } else if (strcspn(dstPath, "_mlc") > 0) { + IOSUHAX_FSA_FlushVolume(fsaFd, "/vol/storage_mlc01"); + } } void restoreSavedata(Title* title, u8 slot, bool allusers, bool common) { @@ -396,8 +601,8 @@ void restoreSavedata(Title* title, u8 slot, bool allusers, bool common) { bool isUSB = title->isTitleOnUSB, isWii = (highID==0x00010000); char srcPath[PATH_SIZE]; char dstPath[PATH_SIZE]; - const char* path = (isWii ? "slccmpt01:/title" : (isUSB ? "storage_usb:/usr/save" : "storage_mlc:/usr/save")); - sprintf(srcPath, "sd:/wiiu/backups/%08x%08x/%u", highID, lowID, slot); + 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"); if (!allusers && !isWii) { char usrPath[16]; @@ -407,37 +612,52 @@ void restoreSavedata(Title* title, u8 slot, bool allusers, bool common) { if (common) { strcpy(srcPath + srcOffset, "/common"); strcpy(dstPath + dstOffset, "/common"); - if (DumpDir(srcPath, dstPath)!=0) promptError("Common save not found."); + if (DumpDir(srcPath, dstPath) != 0) promptError("Common save not found."); } sprintf(srcPath + srcOffset, "/%s", usrPath); sprintf(dstPath + dstOffset, "/%s", usrPath); } - if (DumpDir(srcPath, dstPath)!=0) promptError("Restore failed."); + if (DumpDir(srcPath, dstPath) != 0) promptError("Restore failed."); + if (strcspn(dstPath, "_usb") > 0) { + IOSUHAX_FSA_FlushVolume(fsaFd, "/vol/storage_usb01"); + } else if (strcspn(dstPath, "_mlc") > 0) { + IOSUHAX_FSA_FlushVolume(fsaFd, "/vol/storage_mlc01"); + } } void wipeSavedata(Title* title, bool allusers, bool common) { - if (!promptConfirm("Are you sure?") || !promptConfirm("Hm, are you REALLY sure?")) return; int slotb = getEmptySlot(title->highID, title->lowID); if (slotb>=0 && promptConfirm("Backup current savedata first?")) backupSavedata(title, slotb, allusers, common); u32 highID = title->highID, lowID = title->lowID; bool isUSB = title->isTitleOnUSB, isWii = (highID==0x00010000); char srcPath[PATH_SIZE]; - const char* path = (isWii ? "slccmpt01:/title" : (isUSB ? "storage_usb:/usr/save" : "storage_mlc:/usr/save")); + char origPath[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"); if (!allusers && !isWii) { u32 offset = strlen(srcPath); if (common) { strcpy(srcPath + offset, "/common"); + sprintf(origPath, "%s", srcPath); if (DeleteDir(srcPath)!=0) promptError("Common save not found."); + if (IOSUHAX_FSA_Remove(fsaFd, origPath) != 0) promptError("Failed to delete common folder."); } char usrPath[16]; getUserID(usrPath); sprintf(srcPath + offset, "/%s", usrPath); + sprintf(origPath, "%s", srcPath); } if (DeleteDir(srcPath)!=0) promptError("Failed to delete savefile."); - + if (!allusers && !isWii) { + if (IOSUHAX_FSA_Remove(fsaFd, origPath) != 0) promptError("Failed to delete user folder."); + } + if (strcspn(srcPath, "_usb") > 0) { + IOSUHAX_FSA_FlushVolume(fsaFd, "/vol/storage_usb01"); + } else if (strcspn(srcPath, "_mlc") > 0) { + IOSUHAX_FSA_FlushVolume(fsaFd, "/vol/storage_mlc01"); + } } void importFromLoadiine(Title* title, bool common, int version) { @@ -449,19 +669,19 @@ void importFromLoadiine(Title* title, bool common, int version) { bool isUSB = title->isTitleOnUSB; char srcPath[PATH_SIZE]; char dstPath[PATH_SIZE]; - if (getLoadiineGameSaveDir(srcPath, title->productCode)!=0) return; + if (getLoadiineGameSaveDir(srcPath, title->productCode) !=0 ) return; if (version) sprintf(srcPath + strlen(srcPath), "/v%i", version); char usrPath[16]; getUserID(usrPath); u32 srcOffset = strlen(srcPath); getLoadiineUserDir(srcPath, srcPath, usrPath); - sprintf(dstPath, "storage_%s:/usr/save/%08x/%08x/user", isUSB ? "usb" : "mlc", highID, lowID); + sprintf(dstPath, "/vol/storage_%s01/usr/save/%08x/%08x/user", isUSB ? "usb" : "mlc", highID, lowID); u32 dstOffset = strlen(dstPath); sprintf(dstPath + dstOffset, "/%s", usrPath); - if (DumpDir(srcPath, dstPath)!=0) promptError("Failed to import savedata from loadiine."); + if (DumpDir(srcPath, dstPath) != 0) promptError("Failed to import savedata from loadiine."); strcpy(srcPath + srcOffset, "/c\0"); strcpy(dstPath + dstOffset, "/common\0"); - if (DumpDir(srcPath, dstPath)!=0) promptError("Common save not found."); + if (DumpDir(srcPath, dstPath) != 0) promptError("Common save not found."); } @@ -478,7 +698,7 @@ void exportToLoadiine(Title* title, bool common, int version) { getUserID(usrPath); u32 dstOffset = strlen(dstPath); getLoadiineUserDir(dstPath, dstPath, usrPath); - sprintf(srcPath, "storage_%s:/usr/save/%08x/%08x/user", isUSB ? "usb" : "mlc", highID, lowID); + sprintf(srcPath, "/vol/storage_%s01/usr/save/%08x/%08x/user", isUSB ? "usb" : "mlc", highID, lowID); u32 srcOffset = strlen(srcPath); sprintf(srcPath + srcOffset, "/%s", usrPath); if (DumpDir(srcPath, dstPath)!=0) promptError("Failed to export savedata to loadiine."); diff --git a/src/savemng.h b/src/savemng.h index 0e084ad..f0ed349 100644 --- a/src/savemng.h +++ b/src/savemng.h @@ -29,7 +29,7 @@ typedef struct { void console_print_pos(int x, int y, const char* format, ...); bool promptConfirm(const char* question); -void promptError(const char* message); +void promptError(const char* message, ...); void getUserID(char* out); int getLoadiineGameSaveDir(char* out, const char* productCode); @@ -37,6 +37,7 @@ int getLoadiineSaveVersionList(int* out, const char* gamePath); int getLoadiineUserDir(char* out, const char* fullSavePath, const char* userID); bool isSlotEmpty(u32 highID, u32 lowID, u8 slot); +bool hasCommonSave(Title* title, bool inSD, bool iine, u8 slot, int version); void copySavedata(Title* title, Title* titled, bool allusers, bool common); void backupSavedata(Title* title, u8 slot, bool allusers, bool common); @@ -45,8 +46,11 @@ void wipeSavedata(Title* title, bool allusers, bool common); void importFromLoadiine(Title* title, bool common, int version); void exportToLoadiine(Title* title, bool common, int version); +void setFSAFD(int fd); +int checkEntry(const char * fPath); + #ifdef __cplusplus } #endif -#endif \ No newline at end of file +#endif