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.
This commit is contained in:
GabyPCgeeK
2017-07-15 00:24:33 -04:00
parent d6564ae540
commit f696d3941c
9 changed files with 565 additions and 293 deletions

View File

@@ -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

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<app version="1">
<name>SaveMii</name>
<name>SaveMii Mod</name>
<coder>Ryuzaki_MrL original (GabyPCgeeK mod)</coder>
<version>1.2.0.m2</version>
<release_date>20170707000000</release_date>
<version>1.2.0.mod3</version>
<release_date>20170714000000</release_date>
<short_description>WiiU/vWii Save Manager</short_description>
<long_description>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.</long_description>
<category>tool</category>
<url>https://github.com/Ryuzaki-MrL/savemii/releases</url>
<url>https://github.com/GabyPCgeeK/savemii/releases</url>
</app>

View File

@@ -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

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -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");

View File

@@ -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.");

View File

@@ -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
#endif