Update for v1.1.0

- Fix savedata backup/restore for disc titles
- Separate backups by user
- Add loadiine support
- Move backups folder to sd:/wiiu (you need to move your backups to the
new location)
- Savedata wiping
- Invert task and title selection
- Code clean-up
This commit is contained in:
Lázaro Vieira 2017-04-06 02:35:10 -03:00
parent ba51de6c57
commit 9dc7fb3b8d
6 changed files with 407 additions and 130 deletions

View File

@ -2,7 +2,7 @@
<app version="1">
<name>SaveMii</name>
<coder>Ryuzaki_MrL</coder>
<version>1.0.0</version>
<version>1.1.0</version>
<release_date>20170404000000</release_date>
<short_description>WiiU/vWii Save Manager</short_description>
<long_description>WiiU/vWii Save Manager

View File

@ -69,53 +69,6 @@ void uprintf(int x, int y, const char* format, ...) {
curr_line=curr_line_tmp;
}
void pause(int showmsg) {
while(1) {
if (showmsg) {
OSScreenClearBufferEx(0, 0);
OSScreenClearBufferEx(1, 0);
console_print_pos(0, curr_line, "Press any key to continue...\n");
OSScreenFlipBuffersEx(0);
OSScreenFlipBuffersEx(1);
}
updatePressedButtons();
if (isPressed(0xFFFF)) break;
}
}
int promptConfirm(const char* question) {
ucls();
const char* msg = "(A) Confirm - (B) Cancel";
int ret = 0;
while(1) {
OSScreenClearBufferEx(0, 0);
OSScreenClearBufferEx(1, 0);
console_print_pos(25 - (strlen(question)>>1), 8, question);
console_print_pos(25 - (strlen(msg)>>1), 10, msg);
OSScreenFlipBuffersEx(0);
OSScreenFlipBuffersEx(1);
updatePressedButtons();
if (isPressed(VPAD_BUTTON_A | VPAD_BUTTON_B | VPAD_BUTTON_HOME)) {
ret = isPressed(VPAD_BUTTON_A);
break;
}
}
return ret;
}
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);
updatePressedButtons();
if (isPressed(0xFFFF)) break;
}
}
int64_t uGetTime() {
return OSGetTime()/SECS_TO_TICKS(1);
}

View File

@ -50,9 +50,6 @@ void ucls();
void ScreenInit();
void flipBuffers();
void uprintf(int x, int y, const char* format, ...);
void pause(int showmsg);
int promptConfirm(const char* question);
void promptError(const char* message);
int64_t uGetTime();
void updatePressedButtons();
void updateHeldButtons();

View File

@ -10,24 +10,17 @@
#include "savemng.h"
#define VERSION_MAJOR 1
#define VERSION_MINOR 0
#define VERSION_MINOR 1
#define VERSION_MICRO 0
u8 slot = 0;
bool allusers = 0, common = 1;
int menu = 0, mode = 0, task = 0, targ = 0;
int cursor = 0, scroll = 0;
int titleswiiu = 0, titlesvwii = 0;
typedef struct {
u32 highID;
u32 lowID;
char shortname[256];
bool isTitleOnUSB;
} Title;
//just to be able to call async
void someFunc(void *arg)
{
void someFunc(void *arg) {
(void)arg;
}
@ -59,6 +52,7 @@ void MCPHookClose() {
Title* loadWiiUTitles() {
// Source: haxchi installer
int mcp_handle = MCP_Open();
int count = MCP_TitleCount(mcp_handle);
int listSize = count*0x61;
@ -79,7 +73,7 @@ Title* loadWiiUTitles() {
char* element = tList+(i*0x61);
u32 highID = *(u32*)(element), lowID = *(u32*)(element+4);
if (highID!=0x00050000) continue;
bool isTitleOnUSB = memcmp(element+0x56,"mlc",4)!=0;
bool isTitleOnUSB = (memcmp(element+0x56,"usb",4)==0);
char path[255];
memset(path, 0, 255);
sprintf(path, "storage_%s:/usr/title/%08x/%08x/meta/meta.xml", isTitleOnUSB ? "usb" : "mlc", highID, lowID);
@ -101,11 +95,14 @@ Title* loadWiiUTitles() {
for (cur_node = root_element->children; cur_node; cur_node = cur_node->next) {
if (
(cur_node->type != XML_ELEMENT_NODE) ||
(memcmp(cur_node->name, "shortname_en", 13) != 0) ||
(xmlNodeGetContent(cur_node) == NULL) ||
(!strlen((char*)xmlNodeGetContent(cur_node)))
) continue;
strcpy(titles[titleswiiu].shortname, (char*)xmlNodeGetContent(cur_node));
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));
}
xmlFreeDoc(tmp);
@ -133,8 +130,8 @@ Title* loadWiiUTitles() {
Title* loadWiiTitles() {
struct dirent *dirent = NULL;
DIR *dir = NULL;
struct dirent* dirent = NULL;
DIR* dir = NULL;
dir = opendir("slccmpt01:/title/00010000");
if (dir == NULL) {
@ -168,7 +165,7 @@ Title* loadWiiTitles() {
fread(bnrBuf, 0x02, 0x20, bnrFile);
fclose(bnrFile);
for (int j = 0, k = 0; j < 0x20; j++) {
titles[i].shortname[k++] = (char)bnrBuf[j];
titles[i].shortName[k++] = (char)bnrBuf[j];
}
free(bnrBuf);
}
@ -192,7 +189,7 @@ Title* loadWiiTitles() {
}
void unloadTitles(Title* titles) {
free(titles);
if (titles) free(titles);
}
/* Entry point */
@ -206,6 +203,7 @@ int Menu_Main(void) {
}
if (res < 0) {
promptError("IOSUHAX_Open failed.");
unmount_sd_fat("sd");
return EXIT_SUCCESS;
}
@ -214,6 +212,7 @@ int Menu_Main(void) {
int fsaFd = IOSUHAX_FSA_Open();
if (fsaFd < 0) {
promptError("IOSUHAX_FSA_Open failed.");
unmount_sd_fat("sd");
if (mcp_hook_fd >= 0) MCPHookClose();
else IOSUHAX_Close();
return EXIT_SUCCESS;
@ -226,8 +225,9 @@ int Menu_Main(void) {
ucls();
Title* wiiutitles = loadWiiUTitles();
Title* wiititles = loadWiiTitles();
int* versionList = (int*)malloc(0x100*sizeof(int));
while(1) {
while(wiiutitles!=NULL && wiititles!=NULL) {
OSScreenClearBufferEx(0, 0);
OSScreenClearBufferEx(1, 0);
@ -237,29 +237,46 @@ int Menu_Main(void) {
Title* titles = mode ? wiititles : wiiutitles;
int count = mode ? titlesvwii : titleswiiu;
int entrycount = 0;
switch(menu) {
case 0: { // Main Menu
entrycount = 2;
console_print_pos(0, 2, " Wii U Save Management");
console_print_pos(0, 3, " vWii Save Management");
console_print_pos(0, 2 + cursor, "->");
} break;
case 1: { // WiiU/vWii Save Management
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 + cursor, "->");
} break;
case 2: { // Wii/Wii U Title List
case 1: { // Select Title
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", titles[i+scroll].shortname);
if (strlen(titles[i+scroll].shortName)) console_print_pos(0, i+2, " %s", titles[i+scroll].shortName);
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 3: { // Slot select
console_print_pos(0, 2, "Press LEFT or RIGHT to select slot.");
console_print_pos(0, 3, " < %03u >", slot);
case 2: { // Select Task
entrycount = 3 + 2*(mode==0);
console_print_pos(0, 2, " Backup savedata");
console_print_pos(0, 3, " Restore savedata");
console_print_pos(0, 4, " 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, 2 + cursor, "->");
} break;
case 3: { // Select Options
entrycount = 1 + 2*(mode==0);
console_print_pos(0, 2, "Select %s:", task>2 ? "version" : "slot");
if (task > 2) console_print_pos(0, 3, " < v%u >", versionList ? versionList[slot] : 0);
else console_print_pos(0, 3, " < %03u >", slot);
if (mode==0) {
console_print_pos(0, 5, "Select user:");
console_print_pos(0, 6, " < %s >", (allusers&&(task<3)) ? "all users" : "this user");
console_print_pos(0, 8, "Include 'common' save?");
console_print_pos(0, 9, " < %s >", common ? "yes" : "no ");
console_print_pos(0, 3 + cursor*3, "->");
}
} break;
}
@ -272,8 +289,6 @@ int Menu_Main(void) {
updatePressedButtons();
updateHeldButtons();
int entrycount = ((menu==0) ? 2 : ((menu==1) ? 2 : count));
if (isPressed(VPAD_BUTTON_DOWN) || isHeld(VPAD_BUTTON_DOWN)) {
if (entrycount<=14) cursor = (cursor + 1) % entrycount;
else if (cursor < 6) cursor++;
@ -289,10 +304,22 @@ int Menu_Main(void) {
}
if (isPressed(VPAD_BUTTON_LEFT) || isHeld(VPAD_BUTTON_LEFT)) {
if (menu==3) slot--;
if (menu==3) {
switch(cursor) {
case 0: slot--; break;
case 1: allusers^=1; break;
case 2: common^=1; break;
}
}
usleep(100000);
} else if (isPressed(VPAD_BUTTON_RIGHT) || isHeld(VPAD_BUTTON_RIGHT)) {
if (menu==3) slot++;
if (menu==3) {
switch(cursor) {
case 0: slot++; break;
case 1: allusers^=1; break;
case 2: common^=1; break;
}
}
usleep(100000);
}
@ -300,20 +327,30 @@ int Menu_Main(void) {
ucls();
if (menu<3) {
if (menu==0) mode = cursor;
if (menu==1) task = cursor;
if (menu==2) targ = cursor+scroll;
if (menu==1) targ = cursor+scroll;
if (menu==2) {
task = cursor;
if (task > 2) {
char gamePath[PATH_SIZE];
memset(versionList, 0, 0x100*sizeof(int));
if (getLoadiineGameSaveDir(gamePath, titles[targ].productCode)==0)
getLoadiineSaveVersionList(versionList, gamePath);
}
}
menu++;
cursor = scroll = 0;
} else {
switch(task) {
case 0: backupSavedata(titles[targ].highID, titles[targ].lowID, titles[targ].isTitleOnUSB, slot); break;
case 1: restoreSavedata(titles[targ].highID, titles[targ].lowID, titles[targ].isTitleOnUSB, slot); break;
case 2: wipeSavedata(titles[targ].highID, titles[targ].lowID, titles[targ].isTitleOnUSB); break;
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 3: importFromLoadiine(&titles[targ], common, versionList ? versionList[slot] : 0); break;
case 4: exportToLoadiine(&titles[targ], common, versionList ? versionList[slot] : 0); break;
}
}
} else if (isPressed(VPAD_BUTTON_B)) {
} else if (isPressed(VPAD_BUTTON_B) && menu>0) {
ucls();
if (menu>0) menu--;
menu--;
cursor = scroll = 0;
}
@ -323,6 +360,7 @@ int Menu_Main(void) {
unloadTitles(wiiutitles);
unloadTitles(wiititles);
free(versionList);
fatUnmount("sd");
fatUnmount("usb");

View File

@ -2,15 +2,15 @@
#define BUFFER_SIZE 0x80000
void console_print_pos(int x, int y, const char *format, ...) {
void console_print_pos(int x, int y, const char* format, ...) {
char * tmp = NULL;
char* tmp = NULL;
va_list va;
va_start(va, format);
if ((vasprintf(&tmp, format, va) >= 0) && tmp) {
if(strlen(tmp) > 79) tmp[79] = 0;
if (strlen(tmp) > 79) tmp[79] = 0;
OSScreenPutFontEx(0, x, y, tmp);
OSScreenPutFontEx(1, x, y, tmp);
@ -18,30 +18,64 @@ void console_print_pos(int x, int y, const char *format, ...) {
}
va_end(va);
if(tmp) free(tmp);
if (tmp) free(tmp);
}
int DumpFile(char *pPath, const char * output_path) {
bool promptConfirm(const char* question) {
ucls();
const char* msg = "(A) Confirm - (B) Cancel";
int ret = 0;
while(1) {
OSScreenClearBufferEx(0, 0);
OSScreenClearBufferEx(1, 0);
console_print_pos(25 - (strlen(question)>>1), 8, question);
console_print_pos(25 - (strlen(msg)>>1), 10, msg);
OSScreenFlipBuffersEx(0);
OSScreenFlipBuffersEx(1);
updatePressedButtons();
if (isPressed(VPAD_BUTTON_A | VPAD_BUTTON_B | VPAD_BUTTON_HOME)) {
ret = isPressed(VPAD_BUTTON_A);
break;
}
}
return ret;
}
char *pFilename = strrchr(pPath, '/');
if(!pFilename) pFilename = pPath;
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);
updatePressedButtons();
if (isPressed(0xFFFF)) break;
}
}
// Source: ft2sd
int DumpFile(char* pPath, const char* output_path) {
char* pFilename = strrchr(pPath, '/');
if (!pFilename) pFilename = pPath;
else pFilename++;
unsigned char* dataBuf = (unsigned char*)memalign(0x40, BUFFER_SIZE);
if(!dataBuf) {
if (!dataBuf) {
promptError("Out of memory.");
return -1;
}
FILE *pReadFile = fopen(pPath, "rb");
if(!pReadFile) {
FILE* pReadFile = fopen(pPath, "rb");
if (!pReadFile) {
promptError("Failed to open file.");
return -2;
}
FILE *pWriteFile = fopen(output_path, "wb");
if(!pWriteFile) {
FILE* pWriteFile = fopen(output_path, "wb");
if (!pWriteFile) {
promptError("Failed to create file.");
fclose(pReadFile);
return -3;
@ -50,7 +84,6 @@ int DumpFile(char *pPath, const char * output_path) {
unsigned int size = 0;
unsigned int ret;
// Copy rpl in memory
while ((ret = fread(dataBuf, 0x1, BUFFER_SIZE, pReadFile)) > 0) {
fwrite(dataBuf, 0x01, ret, pWriteFile);
size += ret;
@ -64,10 +97,11 @@ int DumpFile(char *pPath, const char * output_path) {
}
int DumpDir(char *pPath, const char * target_path) {
// Source: ft2sd
int DumpDir(char* pPath, const char* target_path) {
struct dirent *dirent = NULL;
DIR *dir = NULL;
struct dirent* dirent = NULL;
DIR* dir = NULL;
dir = opendir(pPath);
if (dir == NULL) {
@ -82,14 +116,14 @@ int DumpDir(char *pPath, const char * target_path) {
OSScreenClearBufferEx(0, 0);
OSScreenClearBufferEx(1, 0);
if(strcmp(dirent->d_name, "..") == 0 || strcmp(dirent->d_name, ".") == 0) continue;
if (strcmp(dirent->d_name, "..") == 0 || strcmp(dirent->d_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) {
if (dirent->d_type & DT_DIR) {
char *targetPath = (char*)malloc(FS_MAX_FULLPATH_SIZE);
char* targetPath = (char*)malloc(FS_MAX_FULLPATH_SIZE);
snprintf(targetPath, FS_MAX_FULLPATH_SIZE, "%s/%s", target_path, dirent->d_name);
CreateSubfolder(targetPath);
@ -98,7 +132,7 @@ int DumpDir(char *pPath, const char * target_path) {
} else {
char *targetPath = (char*)malloc(FS_MAX_FULLPATH_SIZE);
char* targetPath = (char*)malloc(FS_MAX_FULLPATH_SIZE);
snprintf(targetPath, FS_MAX_FULLPATH_SIZE, "%s/%s", target_path, dirent->d_name);
console_print_pos(0, 0, "Copying file %s", dirent->d_name);
@ -123,28 +157,265 @@ int DumpDir(char *pPath, const char * target_path) {
}
void backupSavedata(u32 highID, u32 lowID, bool isUSB, int slot) {
int DeleteDir(char* pPath) {
char srcPath[256];
char dstPath[256];
const char* path = ((highID==0x00010000) ? "slccmpt01:/title" : (isUSB ? "storage_usb:/usr/save" : "storage_mlc:/usr/save"));
sprintf(srcPath, "%s/%08x/%08x/%s", path, highID, lowID, (highID==0x00010000) ? "data" : "user");
sprintf(dstPath, "sd:/wiiu/apps/savemii/backups/%08x%08x/%i", highID, lowID, slot);
struct dirent* dirent = NULL;
DIR* dir = NULL;
dir = opendir(pPath);
if (dir == NULL) {
promptError("Failed to open directory.");
return -1;
}
while ((dirent = readdir(dir)) != 0) {
OSScreenClearBufferEx(0, 0);
OSScreenClearBufferEx(1, 0);
if (strcmp(dirent->d_name, "..") == 0 || strcmp(dirent->d_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) {
DeleteDir(pPath);
} else {
console_print_pos(0, 0, "Deleting file %s", dirent->d_name);
console_print_pos(0, 1, "From: %s", pPath);
if (remove(pPath)!=0) promptError("Failed to delete file.");
}
OSScreenFlipBuffersEx(0);
OSScreenFlipBuffersEx(1);
pPath[len] = 0;
}
closedir(dir);
return 0;
}
// Source: loadiine_gx2
void getUserID(char* out) {
/* get persistent ID - thanks to Maschell */
unsigned int nn_act_handle;
unsigned long (*GetPersistentIdEx)(unsigned char);
int (*GetSlotNo)(void);
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, "GetSlotNo__Q2_2nn3actFv", &GetSlotNo);
OSDynLoad_FindExport(nn_act_handle, 0, "Initialize__Q2_2nn3actFv", &nn_Initialize);
OSDynLoad_FindExport(nn_act_handle, 0, "Finalize__Q2_2nn3actFv", &nn_Finalize);
nn_Initialize(); // To be sure that it is really Initialized
unsigned char slotno = GetSlotNo();
unsigned int persistentID = GetPersistentIdEx(slotno);
nn_Finalize(); //must be called an equal number of times to nn_Initialize
sprintf(out, "%08X", persistentID);
}
int getLoadiineGameSaveDir(char* out, const char* productCode) {
struct dirent* dirent = NULL;
DIR* dir = NULL;
dir = opendir("sd:/wiiu/saves");
if (dir == NULL) {
promptError("Failed to open directory.");
return -1;
}
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);
return 0;
}
}
promptError("Loadiine game folder not found.");
closedir(dir);
return -2;
}
int getLoadiineSaveVersionList(int* out, const char* gamePath) {
struct dirent* dirent = NULL;
DIR* dir = NULL;
dir = opendir(gamePath);
if (dir == NULL) {
promptError("Loadiine game folder not found.");
return -1;
}
int i = 0;
while ((i < 255) && ((dirent = readdir(dir)) != 0)) {
if ((dirent->d_type & DT_DIR) && (strchr(dirent->d_name, 'v')!=NULL)) {
out[++i] = strtol((dirent->d_name)+1, NULL, 10);
}
}
closedir(dir);
return 0;
}
int getLoadiineUserDir(char* out, const char* fullSavePath, const char* userID) {
struct dirent* dirent = NULL;
DIR* dir = NULL;
dir = opendir(fullSavePath);
if (dir == NULL) {
promptError("Failed to open directory.");
return -1;
}
while ((dirent = readdir(dir)) != 0) {
if ((dirent->d_type & DT_DIR) && (strstr(dirent->d_name, userID))) {
sprintf(out, "%s/%s", fullSavePath, dirent->d_name);
closedir(dir);
return 0;
}
}
sprintf(out, "%s/u", fullSavePath);
closedir(dir);
return 0;
}
void backupSavedata(Title* title, u8 slot, bool allusers, bool common) {
u32 highID = title->highID, lowID = title->lowID;
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, "%s/%08x/%08x/%s", path, highID, lowID, isWii ? "data" : "user");
sprintf(dstPath, "sd:/wiiu/backups/%08x%08x/%u", highID, lowID, slot);
if (!allusers && !isWii) {
char usrPath[16];
getUserID(usrPath);
sprintf(dstPath + strlen(dstPath), "/%s", usrPath);
u32 offset = strlen(srcPath);
if (common) {
strcpy(srcPath + offset, "/common");
if (DumpDir(srcPath, dstPath)!=0) promptError("Common save not found.");
}
sprintf(srcPath + offset, "/%s", usrPath);
}
DumpDir(srcPath, dstPath);
}
void restoreSavedata(u32 highID, u32 lowID, bool isUSB, int slot) {
void restoreSavedata(Title* title, u8 slot, bool allusers, bool common) {
char srcPath[256];
char dstPath[256];
const char* path = ((highID==0x00010000) ? "slccmpt01:/title" : (isUSB ? "storage_usb:/usr/save" : "storage_mlc:/usr/save"));
sprintf(srcPath, "sd:/wiiu/apps/savemii/backups/%08x%08x/%i", highID, lowID, slot);
sprintf(dstPath, "%s/%08x/%08x/%s", path, highID, lowID, (highID==0x00010000) ? "data" : "user");
if (!promptConfirm("Are you sure?")) return;
u32 highID = title->highID, lowID = title->lowID;
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);
sprintf(dstPath, "%s/%08x/%08x/%s", path, highID, lowID, isWii ? "data" : "user");
if (!allusers && !isWii) {
char usrPath[16];
getUserID(usrPath);
sprintf(dstPath + strlen(dstPath), "/%s", usrPath);
u32 offset = strlen(srcPath);
if (common) {
strcpy(srcPath + offset, "/common");
if (DumpDir(srcPath, dstPath)!=0) promptError("Common save not found.");
}
sprintf(srcPath + offset, "/%s", usrPath);
}
DumpDir(srcPath, dstPath);
}
void wipeSavedata(u32 highID, u32 lowID, bool isUSB) {
}
void wipeSavedata(Title* title, bool allusers, bool common) {
if (!promptConfirm("Are you sure?") || !promptConfirm("Hm, are you REALLY sure?")) return;
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"));
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");
if (DeleteDir(srcPath)!=0) promptError("Common save not found.");
}
char usrPath[16];
getUserID(usrPath);
sprintf(srcPath + offset, "/%s", usrPath);
}
DeleteDir(srcPath);
}
void importFromLoadiine(Title* title, bool common, int version) {
if (!promptConfirm("Are you sure?")) return;
u32 highID = title->highID, lowID = title->lowID;
bool isUSB = title->isTitleOnUSB;
char srcPath[PATH_SIZE];
char dstPath[PATH_SIZE];
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);
u32 dstOffset = strlen(dstPath);
sprintf(dstPath + dstOffset, "/%s", usrPath);
DumpDir(srcPath, dstPath);
strcpy(srcPath + srcOffset, "/c");
strcpy(dstPath + dstOffset, "/common");
if (DumpDir(srcPath, dstPath)!=0) promptError("Common save not found.");
}
void exportToLoadiine(Title* title, bool common, int version) {
if (!promptConfirm("Are you sure?")) return;
u32 highID = title->highID, lowID = title->lowID;
bool isUSB = title->isTitleOnUSB;
char srcPath[PATH_SIZE];
char dstPath[PATH_SIZE];
if (getLoadiineGameSaveDir(dstPath, title->productCode)!=0) return;
if (version) sprintf(dstPath + strlen(dstPath), "/v%u", version);
char usrPath[16];
getUserID(usrPath);
u32 dstOffset = strlen(dstPath);
getLoadiineUserDir(dstPath, dstPath, usrPath);
sprintf(srcPath, "storage_%s:/usr/save/%08x/%08x/user", isUSB ? "usb" : "mlc", highID, lowID);
u32 srcOffset = strlen(srcPath);
sprintf(srcPath + srcOffset, "/%s", usrPath);
DumpDir(srcPath, dstPath);
strcpy(dstPath + dstOffset, "/c");
strcpy(srcPath + srcOffset, "/common");
if (DumpDir(srcPath, dstPath)!=0) promptError("Common save not found.");
}

View File

@ -10,15 +10,33 @@
#include "lib_easy.h"
#define PATH_SIZE 0x200
#ifdef __cplusplus
extern "C" {
#endif
void console_print_pos(int x, int y, const char *format, ...);
typedef struct {
u32 highID;
u32 lowID;
char shortName[256];
char productCode[32];
bool isTitleOnUSB;
} Title;
void backupSavedata(u32 highID, u32 lowID, bool isUSB, int slot);
void restoreSavedata(u32 highID, u32 lowID, bool isUSB, int slot);
void wipeSavedata(u32 highID, u32 lowID, bool isUSB);
void console_print_pos(int x, int y, const char* format, ...);
bool promptConfirm(const char* question);
void promptError(const char* message);
int getLoadiineGameSaveDir(char* out, const char* productCode);
int getLoadiineSaveVersionList(int* out, const char* gamePath);
int getLoadiineUserDir(char* out, const char* fullSavePath, const char* userID);
void backupSavedata(Title* title, u8 slot, bool allusers, bool common);
void restoreSavedata(Title* title, u8 slot, bool allusers, bool common);
void wipeSavedata(Title* title, bool allusers, bool common);
void importFromLoadiine(Title* title, bool common, int version);
void exportToLoadiine(Title* title, bool common, int version);
#ifdef __cplusplus
}