mirror of
https://github.com/Ryuzaki-MrL/savemii.git
synced 2026-03-22 02:04:07 -05:00
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:
parent
ba51de6c57
commit
9dc7fb3b8d
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
118
src/main.c
118
src/main.c
|
|
@ -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");
|
||||
|
|
|
|||
341
src/savemng.c
341
src/savemng.c
|
|
@ -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.");
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user