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