diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 356bbf4..e3a2ee1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Docker Layer Caching @@ -22,13 +22,13 @@ jobs: docker build . --file Dockerfile --tag builder docker run --rm -v ${PWD}:/project builder make release -j$(nproc) - name: Upload Aroma version - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: SaveMiiModWUTPort-Aroma path: SaveMiiModWUTPort-Aroma.zip if-no-files-found: warn - name: Upload HBL version - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: SaveMiiModWUTPort-HBL path: SaveMiiModWUTPort-HBL.zip diff --git a/Dockerfile b/Dockerfile index c736529..e42cf34 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ -FROM ghcr.io/wiiu-env/devkitppc:20231112 +FROM ghcr.io/wiiu-env/devkitppc:20240704 -COPY --from=ghcr.io/wiiu-env/libmocha:20231127 /artifacts $DEVKITPRO +COPY --from=ghcr.io/wiiu-env/libmocha:20240603 /artifacts $DEVKITPRO RUN git clone --recursive https://github.com/yawut/libromfs-wiiu --single-branch && \ cd libromfs-wiiu && \ diff --git a/Makefile b/Makefile index 596886d..a263acb 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ TOPDIR ?= $(CURDIR) #------------------------------------------------------------------------------- APP_NAME := SaveMii WUT Port APP_SHORTNAME := SaveMii -APP_AUTHOR := DaThinkingChair +APP_AUTHOR := DaThinkingChair,w3irDv include $(DEVKITPRO)/wut/share/wut_rules diff --git a/include/BackupSetList.h b/include/BackupSetList.h new file mode 100644 index 0000000..0e5beaa --- /dev/null +++ b/include/BackupSetList.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include + +class BackupSetList { +public: + friend class BackupSetListState; + + BackupSetList() {}; + BackupSetList(const char *backupSetListRoot); + static std::unique_ptr currentBackupSetList; + + void sort(bool sortAscending = false); + std::string at(int i); + void add(std::string backupSet); + + static const std::string ROOT_BS; + static std::string getBackupSetSubPath() { return backupSetSubPath; } + static std::string getBackupSetEntry() { return backupSetEntry; } + static void setBackupSetEntry(int i); + static void setBackupSetSubPath(); + static void initBackupSetList(); + static void setBackupSetSubPathToRoot() { backupSetSubPath = "/"; } + static void saveBackupSetSubPath() { savedBackupSetSubPath = backupSetSubPath; } + static void restoreBackupSetSubPath() { backupSetSubPath = savedBackupSetSubPath; } + +private: + static bool sortAscending; + std::vector backupSets; + int entries; + std::string backupSetListRoot; + static std::string backupSetSubPath; + static std::string backupSetEntry; + static std::string savedBackupSetSubPath; + +}; + diff --git a/include/Metadata.h b/include/Metadata.h new file mode 100644 index 0000000..20ed622 --- /dev/null +++ b/include/Metadata.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +class Metadata { +public: + Metadata(uint32_t high, uint32_t low, uint8_t s) : highID(high), + lowID(low), + slot(s), + path (getDynamicBackupPath(highID, lowID, slot).append("/savemiiMeta.json")) { + } + + Metadata(uint32_t high, uint32_t low, uint8_t s, std::string datetime) : highID(high), + lowID(low), + slot(s), + path (getBatchBackupPath(highID, lowID, slot, datetime).append("/savemiiMeta.json")) { + } + + std::string get(); + bool set(const std::string &date,bool isUSB); + static std::string serialId; + +private: + uint32_t highID; + uint32_t lowID; + uint8_t slot; + std::string path; +}; \ No newline at end of file diff --git a/include/date.h b/include/date.h deleted file mode 100644 index b5b8134..0000000 --- a/include/date.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include - -class Date { -public: - Date(uint32_t high, uint32_t low, uint8_t s) : highID(high), - lowID(low), - slot(s), - path(StringUtils::stringFormat("sd:/wiiu/backups/%08x%08x/%u/savemiiMeta.json", highID, lowID, slot)) { - } - std::string get(); - bool set(const std::string &date); - -private: - uint32_t highID; - uint32_t lowID; - uint8_t slot; - std::string path; -}; \ No newline at end of file diff --git a/include/menu/BackupSetListState.h b/include/menu/BackupSetListState.h new file mode 100644 index 0000000..8aea7cc --- /dev/null +++ b/include/menu/BackupSetListState.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + + + + +class BackupSetListState : public ApplicationState { +public: + BackupSetListState(); + static void resetCursorPosition(); + enum eState { + STATE_BACKUPSET_MENU, + STATE_DO_SUBSTATE, + }; + + void render() override; + ApplicationState::eSubState update(Input *input) override; + +private: + std::unique_ptr subState{}; + eState state = STATE_BACKUPSET_MENU; + + bool sortAscending; + + std::string backupSetListRoot; +}; \ No newline at end of file diff --git a/include/menu/BatchBackupState.h b/include/menu/BatchBackupState.h index 1af5b54..c919d09 100644 --- a/include/menu/BatchBackupState.h +++ b/include/menu/BatchBackupState.h @@ -19,6 +19,7 @@ class BatchBackupState : public ApplicationState { void render() override; ApplicationState::eSubState update(Input *input) override; + private: std::unique_ptr subState{}; eState state = STATE_BATCH_BACKUP; diff --git a/include/menu/TitleOptionsState.h b/include/menu/TitleOptionsState.h index 082c60c..29b3edc 100644 --- a/include/menu/TitleOptionsState.h +++ b/include/menu/TitleOptionsState.h @@ -7,13 +7,13 @@ class TitleOptionsState : public ApplicationState { public: - TitleOptionsState(Title title, Task task, int *versionList, int8_t sdusers, int8_t allusers, bool common, int8_t allusers_d, Title *titles, int titleCount) : title(title), + TitleOptionsState(Title title, Task task, int *versionList, int8_t sduser, int8_t wiiuuser, bool common, int8_t wiiuuser_d, Title *titles, int titleCount) : title(title), task(task), versionList(versionList), - sdusers(sdusers), - allusers(allusers), + sduser(sduser), + wiiuuser(wiiuuser), common(common), - allusers_d(allusers_d), + wiiuuser_d(wiiuuser_d), titles(titles), titleCount(titleCount) {} @@ -34,10 +34,10 @@ class TitleOptionsState : public ApplicationState { int *versionList; - int8_t sdusers; - int8_t allusers; + int8_t sduser; + int8_t wiiuuser; bool common; - int8_t allusers_d; + int8_t wiiuuser_d; Title *titles; int titleCount; diff --git a/include/savemng.h b/include/savemng.h index 9152106..ffb1392 100644 --- a/include/savemng.h +++ b/include/savemng.h @@ -108,18 +108,25 @@ std::string getUSB(); void consolePrintPos(int x, int y, const char *format, ...) __attribute__((hot)); bool promptConfirm(Style st, const std::string &question); void promptError(const char *message, ...); +std::string getDynamicBackupPath(uint32_t highID, uint32_t lowID, uint8_t slot); +std::string getBatchBackupPath(uint32_t highID, uint32_t lowID, uint8_t slot, std::string datetime); void getAccountsWiiU(); void getAccountsSD(Title *title, uint8_t slot); bool hasAccountSave(Title *title, bool inSD, bool iine, uint32_t user, uint8_t slot, int version); bool getLoadiineGameSaveDir(char *out, const char *productCode, const char *longName, const uint32_t highID, const uint32_t lowID); bool getLoadiineSaveVersionList(int *out, const char *gamePath); bool isSlotEmpty(uint32_t highID, uint32_t lowID, uint8_t slot); +bool isSlotEmpty(uint32_t highID, uint32_t lowID, uint8_t slot, const std::string &batchDatetime); bool hasCommonSave(Title *title, bool inSD, bool iine, uint8_t slot, int version); -void copySavedata(Title *title, Title *titled, int8_t allusers, int8_t allusers_d, bool common) __attribute__((hot)); -void backupAllSave(Title *titles, int count, OSCalendarTime *date) __attribute__((hot)); -void backupSavedata(Title *title, uint8_t slot, int8_t allusers, bool common) __attribute__((hot)); -void restoreSavedata(Title *title, uint8_t slot, int8_t sdusers, int8_t allusers, bool common) __attribute__((hot)); -void wipeSavedata(Title *title, int8_t allusers, bool common) __attribute__((hot)); +void copySavedata(Title *title, Title *titled, int8_t wiiuuser, int8_t wiiuuser_d, bool common) __attribute__((hot)); +std::string getNowDateForFolder() __attribute__((hot)); +std::string getNowDate() __attribute__((hot)); +void writeMetadata(uint32_t highID,uint32_t lowID,uint8_t slot,bool isUSB) __attribute__((hot)); +void writeMetadata(uint32_t highID,uint32_t lowID,uint8_t slot,bool isUSB,const std::string &batchDatetime) __attribute__((hot)); +void backupAllSave(Title *titles, int count, const std::string &batchDatetime) __attribute__((hot)); +void backupSavedata(Title *title, uint8_t slot, int8_t wiiuuser, bool common) __attribute__((hot)); +void restoreSavedata(Title *title, uint8_t slot, int8_t sduser, int8_t wiiuuser, bool common) __attribute__((hot)); +void wipeSavedata(Title *title, int8_t wiiuuser, bool common) __attribute__((hot)); void importFromLoadiine(Title *title, bool common, int version); void exportToLoadiine(Title *title, bool common, int version); int checkEntry(const char *fPath); diff --git a/include/utils/Colors.h b/include/utils/Colors.h index 3e24658..8279c86 100644 --- a/include/utils/Colors.h +++ b/include/utils/Colors.h @@ -3,4 +3,7 @@ #define COLOR_WHITE Color(0xffffffff) #define COLOR_BLACK Color(0, 0, 0, 255) #define COLOR_BACKGROUND Color(0x00006F00) -#define COLOR_TEXT COLOR_WHITE \ No newline at end of file +#define COLOR_TEXT COLOR_WHITE +#define COLOR_LIST_HIGH Color(0x40D050FF) +#define COLOR_LIST Color(0x00FF00FF) +#define COLOR_INFO Color(0x88CC88FF) \ No newline at end of file diff --git a/include/version.h b/include/version.h index c784e46..18c9efd 100644 --- a/include/version.h +++ b/include/version.h @@ -2,4 +2,4 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 6 -#define VERSION_MICRO 2 +#define VERSION_MICRO 3 diff --git a/languages/SChinese.json b/languages/SChinese.json index 0176107..3eff18b 100644 --- a/languages/SChinese.json +++ b/languages/SChinese.json @@ -72,9 +72,9 @@ "Filesize: %d bytes": "文件大小:%d 字节", "Deleting folder %s": "正在删除 %s 文件夹", "From: \n%s": "从:\n%s", - "Failed to delete folder %s\n%s": "删除文件夹失败 %s\n%s", + "Failed to delete folder %s: %s": "删除文件夹失败 %s: %s", "Deleting file %s": "正在删除文件 %s", - "Failed to delete file %s\n%s": "删除文件失败 %s\n%s", + "Failed to delete file %s: %s": "删除文件失败 %s: %s", "Loadiine game folder not found.": "Loadiine游戏文件夹没有发现。", "Failed to open Loadiine game save directory.": "Loadiine游戏文件夹无法打开。", "Are you sure?": "你确定吗?", @@ -90,9 +90,9 @@ "Restore failed.": "还原保存数据失败。", "Hm, are you REALLY sure?": "嘶,你真的真的要这样做?", "Backup current savedata first?": "要不要先备份现在的保存数据?", - "Failed to delete common folder.\n%s": "删除common文件夹失败。\n%s", + "Failed to delete common folder: %s": "删除common文件夹失败: %s", "Failed to delete savefile.": "删除保存数据失败。", - "Failed to delete user folder.\n%s": "删除user文件夹失败。\n%s", + "Failed to delete user folder: %s": "删除user文件夹失败: %s", "Failed to import savedata from loadiine.": "导入loadiine保存数据失败。", "Failed to export savedata to loadiine.": "导出保存数据到loadiine失败。" } diff --git a/languages/TChinese.json b/languages/TChinese.json index a47bf12..a9a56ef 100644 --- a/languages/TChinese.json +++ b/languages/TChinese.json @@ -72,9 +72,9 @@ "Filesize: %d bytes": "資料大小:%d 個字節", "Deleting folder %s": "刪除資料夾 %s", "From: \n%s": "從:\n%s", - "Failed to delete folder %s\n%s": "無法刪除資料夾 %s\n%s", + "Failed to delete folder %s: %s": "無法刪除資料夾 %s: %s", "Deleting file %s": "刪除資料 %s", - "Failed to delete file %s\n%s": "無法刪除資料 %s\n%s", + "Failed to delete file %s: %s": "無法刪除資料 %s: %s", "Loadiine game folder not found.": "未找到Loadiine遊戲資料夾。", "Failed to open Loadiine game save directory.": "打開Loadiine遊戲保存資料夾失敗。", "Are you sure?": "你確定嗎?", @@ -90,9 +90,9 @@ "Restore failed.": "還原失敗。", "Hm, are you REALLY sure?": "蛤?你真的確定嗎?", "Backup current savedata first?": "先備份當前的保存數據?", - "Failed to delete common folder.\n%s": "無法刪除Common資料夾。\n%s", + "Failed to delete common folder: %s": "無法刪除Common資料夾: %s", "Failed to delete savefile.": "無法刪除保存資料。", - "Failed to delete user folder.\n%s": "無法刪除使用者資料夾。\n%s", + "Failed to delete user folder: %s": "無法刪除使用者資料夾: %s", "Failed to import savedata from loadiine.": "無法從loadiine導入保存數據。", "Failed to export savedata to loadiine.": "無法導出保存數據到Loadiine。" } diff --git a/languages/english.json b/languages/english.json index 74d67c2..030d62e 100644 --- a/languages/english.json +++ b/languages/english.json @@ -48,7 +48,6 @@ "yes": "yes", "no ": "no ", "No 'common' save found.": "No 'common' save found.", - "\ue000: Restore \ue001: Back": "\ue000: Restore \ue001: Back", "\ue000: Wipe \ue001: Back": "\ue000: Wipe \ue001: Back", "\ue000: Import \ue001: Back": "\ue000: Import \ue001: Back", "\ue000: Export \ue001: Back": "\ue000: Export \ue001: Back", @@ -72,9 +71,9 @@ "Filesize: %d bytes": "Filesize: %d bytes", "Deleting folder %s": "Deleting folder %s", "From: \n%s": "From: \n%s", - "Failed to delete folder %s\n%s": "Failed to delete folder %s\n%s", + "Failed to delete folder %s: %s": "Failed to delete folder %s: %s", "Deleting file %s": "Deleting file %s", - "Failed to delete file %s\n%s": "Failed to delete file %s\n%s", + "Failed to delete file %s: %s": "Failed to delete file %s: %s", "Loadiine game folder not found.": "Loadiine game folder not found.", "Failed to open Loadiine game save directory.": "Failed to open Loadiine game save directory.", "Are you sure?": "Are you sure?", @@ -90,9 +89,19 @@ "Restore failed.": "Restore failed.", "Hm, are you REALLY sure?": "Hm, are you REALLY sure?", "Backup current savedata first?": "Backup current savedata first?", - "Failed to delete common folder.\n%s": "Failed to delete common folder.\n%s", + "Failed to delete common folder: %s": "Failed to delete common folder: %s", "Failed to delete savefile.": "Failed to delete savefile.", - "Failed to delete user folder.\n%s": "Failed to delete user folder.\n%s", + "Failed to delete user folder: %s": "Failed to delete user folder: %s", "Failed to import savedata from loadiine.": "Failed to import savedata from loadiine.", - "Failed to export savedata to loadiine.": "Failed to export savedata to loadiine." + "Failed to export savedata to loadiine.": "Failed to export savedata to loadiine.", + "\ue083 Sort: %s \ue084": "\ue083 Sort: %s \ue084", + "\ue000: Select BackupSet \ue001: Back": "\ue000: Select BackupSet \ue001: Back", + "\ue002: Change BackupSet \ue000: Restore \ue001: Back": "\ue002: Change BackupSet \ue000: Restore \ue001: Back", + "WiiU USB Savedata >> slot 0": "WiiU USB Savedata >> slot 0", + "WiiU NAND Savedata >> slot 1": "WiiU NAND Savedata >> slot 1", + "vWii Savedata >> slot 0": "vWii Savedata >> slot 0", + "BackupSet: %s": "BackupSet: %s", + ", from ": ", from ", + "Common save not restored.": "Common save not restored.", + "\ue001: Back": "\ue001: Back" } \ No newline at end of file diff --git a/languages/german.json b/languages/german.json index 7089b6a..c05b974 100644 --- a/languages/german.json +++ b/languages/german.json @@ -48,7 +48,6 @@ "yes": "Ja", "no ": "Nein ", "No 'common' save found.": "Keine 'gemeinsame' Speicherung gefunden.", - "\ue000: Restore \ue001: Back": "\ue000: Wiederherstellen \ue001: Zurück", "\ue000: Wipe \ue001: Back": "\ue000: Lösche \ue001: Zurück", "\ue000: Import \ue001: Back": "\ue000: Importieren \ue001: Zurück", "\ue000: Export \ue001: Back": "\ue000: Exportieren \ue001: Zurück", @@ -72,9 +71,9 @@ "Filesize: %d bytes": "Dateigröße: %d bytes", "Deleting folder %s": "Lösche Ordner %s", "From: \n%s": "Von: \n%s", - "Failed to delete folder %s\n%s": "Löschen von Ordner %s\n%s fehlgeschlagen", + "Failed to delete folder %s: %s": "Löschen von Ordner %s: %s fehlgeschlagen", "Deleting file %s": "Lösche Datei %s", - "Failed to delete file %s\n%s": "Löschen von Datei %s\n%s fehlgeschlagen", + "Failed to delete file %s: %s": "Löschen von Datei %s: %s fehlgeschlagen", "Loadiine game folder not found.": "Loadiine Spielordner nicht gefunden.", "Failed to open Loadiine game save directory.": "Fehlgeschlagen Loadiine Spielspeicherverzeichnis zu öffnen.", "Are you sure?": "Bist du sicher?", @@ -90,10 +89,19 @@ "Restore failed.": "Wiederherstellen fehlgeschlagen.", "Hm, are you REALLY sure?": "Hm, are you REALLY sure?", "Backup current savedata first?": "Sichere aktuellen Speicherstand zuerst?", - "Failed to delete common folder.\n%s": "Löschen von 'gemeinsamem' Ordner fehlgeschlagen.\n%s", + "Failed to delete common folder: %s": "Löschen von 'gemeinsamem' Ordner fehlgeschlagen: %s", "Failed to delete savefile.": "Löschen von Speicherstand fehlgeschlagen.", - "Failed to delete user folder.\n%s": "Löschen vom Nutzerordner fehlgeschlagen.\n%s", + "Failed to delete user folder: %s": "Löschen vom Nutzerordner fehlgeschlagen: %s", "Failed to import savedata from loadiine.": "Importieren von Loadiine Speicherdaten fehlgeschlagen.", "Failed to export savedata to loadiine.": "Exportieren von Loadiine Speicherdaten fehlgeschlagen.", - "German": "Deutsch" + "\ue083 Sort: %s \ue084": "\ue083 Sortieren: %s \ue084", + "\ue000: Select BackupSet \ue001: Back": "\ue000: Wählen Sie BackupSet \ue001: Zurück", + "\ue002: Change BackupSet \ue000: Restore \ue001: Back": "\ue002: BackupSet ändern \ue000: Wiederherstellen \ue001: Zurück", + "WiiU USB Savedata >> slot 0": "WiiU USB Savedata >> slot 0", + "WiiU NAND Savedata >> slot 1": "WiiU NAND Savedata >> slot 1", + "vWii Savedata >> slot 0": "vWii Savedata >> slot 0", + "BackupSet: %s": "BackupSet: %s", + ", from ": ", aus ", + "Common save not restored.": "'Gemeinsame' Sicherung nicht wiederhergestellt", + "\ue001: Back": "\ue001: Zurück" } diff --git a/languages/italian.json b/languages/italian.json index 3bca5ef..8cdd6a2 100644 --- a/languages/italian.json +++ b/languages/italian.json @@ -48,7 +48,6 @@ "yes": "sì", "no ": "no ", "No 'common' save found.": "Nessun salvataggio 'comune' trovato.", - "\ue000: Restore \ue001: Back": "\ue000: Ripristina \ue001: Indietro", "\ue000: Wipe \ue001: Back": "\ue000: Pulire \ue001: Indietro", "\ue000: Import \ue001: Back": "\ue000: Importa \ue001: Indietro", "\ue000: Export \ue001: Back": "\ue000: Esporta \ue001: Indietro", @@ -72,9 +71,9 @@ "Filesize: %d bytes": "Dimensione file: %d byte", "Deleting folder %s": "Eliminazione cartella %s", "From: \n%s": "Da: \n%s", - "Failed to delete folder %s\n%s": "Non è stato possibile eliminare il file %s\n%s", + "Failed to delete folder %s: %s": "Non è stato possibile eliminare il file %s: %s", "Deleting file %s": "Eliminazione cartella %s", - "Failed to delete file %s\n%s": "Non è stato possibile eliminare il file %s\n%s", + "Failed to delete file %s: %s": "Non è stato possibile eliminare il file %s: %s", "Loadiine game folder not found.": "Cartella di gioco Loadiine non trovata.", "Failed to open Loadiine game save directory.": "Apertura della directory di salvataggio del gioco Loadiine non riuscita.", "Are you sure?": "Sei sicuro?", @@ -90,9 +89,19 @@ "Restore failed.": "Ripristino fallito.", "Hm, are you REALLY sure?": "Sei DAVVERO sicuro?", "Backup current savedata first?": "Salvare prima il salvataggio corrente?", - "Failed to delete common folder.\n%s": "Impossibile eliminare la cartella comune.\n%s", + "Failed to delete common folder: %s": "Impossibile eliminare la cartella comune: %s", "Failed to delete savefile.": "Non è stato possibile eliminare il salvataggio.", - "Failed to delete user folder.\n%s": "Impossibile eliminare la cartella.\n%s", + "Failed to delete user folder: %s": "Impossibile eliminare la cartella: %s", "Failed to import savedata from loadiine.": "Impossibile importare salvataggi da loadiine.", - "Failed to export savedata to loadiine.": "Impossibile importare salvataggi da loadiine." + "Failed to export savedata to loadiine.": "Impossibile importare salvataggi da loadiine.", + "\ue083 Sort: %s \ue084": "\ue083 Ordinare: %s \ue084", + "\ue000: Select BackupSet \ue001: Back": "\ue000: Seleziona BackupSet \ue001: Indietro", + "\ue002: Change BackupSet \ue000: Restore \ue001: Back": "\ue002: Modificare BackupSet \ue000: Ripristina \ue001: Indietro", + "WiiU USB Savedata >> slot 0": "WiiU USB Savedata >> slot 0", + "WiiU NAND Savedata >> slot 1": "WiiU NAND Savedata >> slot 1", + "vWii Savedata >> slot 0": "vWii Savedata >> slot 0", + "BackupSet: %s": "BackupSet: %s", + ", from ": ", da ", + "Common save not restored.": "Salvataggio comune non ripristinato", + "\ue001: Back": "\ue001: Indietro" } \ No newline at end of file diff --git a/languages/japanese.json b/languages/japanese.json index ca157d0..ffcf765 100644 --- a/languages/japanese.json +++ b/languages/japanese.json @@ -72,9 +72,9 @@ "Filesize: %d bytes": "ファイルサイズ: %d バイト", "Deleting folder %s": "フォルダ %s を削除しています", "From: \n%s": "差出人: \n%s", - "Failed to delete folder %s\n%s": "フォルダ %s の削除に失敗しました\n%s", + "Failed to delete folder %s: %s": "フォルダ %s の削除に失敗しました: %s", "Deleting file %s": "ファイル %s を削除しています", - "Failed to delete file %s\n%s": "ファイル %s の削除に失敗しました\n%s", + "Failed to delete file %s: %s": "ファイル %s の削除に失敗しました: %s", "Loadiine game folder not found.": "読み込み中のゲーム フォルダが見つかりません。", "Failed to open Loadiine game save directory.": "Loadiine ゲームの保存ディレクトリを開けませんでした。", "Are you sure?": "本当によろしいですか?", @@ -90,9 +90,9 @@ "Restore failed.": "復元に失敗しました。", "Hm, are you REALLY sure?": "うーん、本当によろしいですか?", "Backup current savedata first?": "最初に現在のセーブデータをバックアップしますか?", - "Failed to delete common folder.\n%s": "共通フォルダの削除に失敗しました.\n%s", + "Failed to delete common folder: %s": "共通フォルダの削除に失敗しました: %s", "Failed to delete savefile.": "保存ファイルの削除に失敗しました。", - "Failed to delete user folder.\n%s": "ユーザー フォルダの削除に失敗しました。\n%s", + "Failed to delete user folder: %s": "ユーザー フォルダの削除に失敗しました: %s", "Failed to import savedata from loadiine.": "loadiine からセーブデータをインポートできませんでした。", "Failed to export savedata to loadiine.": "セーブデータを loadiine にエクスポートできませんでした。" } \ No newline at end of file diff --git a/languages/korean.json b/languages/korean.json index 85364d5..db16479 100644 --- a/languages/korean.json +++ b/languages/korean.json @@ -72,9 +72,9 @@ "Filesize: %d bytes": "파일 크기: %d 바이트", "Deleting folder %s": "%s 폴더 삭제 중", "From: \n%s": "%s\n에서", - "Failed to delete folder %s\n%s": "%s 폴더 삭제 실패\n%s", + "Failed to delete folder %s: %s": "%s 폴더 삭제 실패: %s", "Deleting file %s": "%s 파일 삭제 중", - "Failed to delete file %s\n%s": "%s 파일 삭제 실패\n%s", + "Failed to delete file %s: %s": "%s 파일 삭제 실패: %s", "Loadiine game folder not found.": "Loadiine 게임 폴더를 찾을 수 없습니다.", "Failed to open Loadiine game save directory.": "Loadiine 게임 저장 디렉토리를 열지 못했습니다.", "Are you sure?": "확실하나요?", @@ -90,9 +90,9 @@ "Restore failed.": "복원에 실패했습니다.", "Hm, are you REALLY sure?": "흠, 정말 확신하나요?", "Backup current savedata first?": "현재 저장 데이터를 먼저 백업하겠습니까?", - "Failed to delete common folder.\n%s": "공통 폴더를 삭제하지 못했습니다.\n%s", + "Failed to delete common folder: %s": "공통 폴더를 삭제하지 못했습니다: %s", "Failed to delete savefile.": "저장파일을 삭제하지 못했습니다.", - "Failed to delete user folder.\n%s": "사용자 폴더를 삭제하지 못했습니다.\n%s", + "Failed to delete user folder: %s": "사용자 폴더를 삭제하지 못했습니다: %s", "Failed to import savedata from loadiine.": "loadiine에서 저장 데이터를 가져오지 못했습니다.", "Failed to export savedata to loadiine.": "저장 데이터를 loadiine으로 내보내지 못했습니다." } \ No newline at end of file diff --git a/languages/portuguese.json b/languages/portuguese.json index 468b5c1..18c3603 100644 --- a/languages/portuguese.json +++ b/languages/portuguese.json @@ -49,7 +49,6 @@ "yes": "sim", "no ": "não ", "No 'common' save found.": "Save 'common' não encontrado.", - "\ue000: Restore \ue001: Back": "\ue000: Restaurar \ue001: Voltar", "\ue000: Wipe \ue001: Back": "\ue000: Excluir \ue001: Voltar", "\ue000: Import \ue001: Back": "\ue000: Importar \ue001: Voltar", "\ue000: Export \ue001: Back": "\ue000: Exportar \ue001: Voltar", @@ -73,9 +72,9 @@ "Filesize: %d bytes": "Tamanho: %d bytes", "Deleting folder %s": "Excluindo pasta %s", "From: \n%s": "De:\n%s", - "Failed to delete folder %s\n%s": "Falha ao excluir pasta %s\n%s", + "Failed to delete folder %s: %s": "Falha ao excluir pasta %s: %s", "Deleting file %s": "Excluindo arquivo %s", - "Failed to delete file %s\n%s": "Falha ao excluir arquivo %s\n%s", + "Failed to delete file %s: %s": "Falha ao excluir arquivo %s: %s", "Loadiine game folder not found.": "Pasta do jogo não encontrada (Loadiine).", "Failed to open Loadiine game save directory.": "Falha ao abrir pasta de save (Loadiine).", "Are you sure?": "Tem certeza?", @@ -91,9 +90,19 @@ "Restore failed.": "Falha ao restaurar.", "Hm, are you REALLY sure?": "Hum, você quer REALMENTE fazer isso?", "Backup current savedata first?": "Deseja fazer backup do save?", - "Failed to delete common folder.\n%s": "Falha ao excluir pasta 'common'.\n%s", + "Failed to delete common folder: %s": "Falha ao excluir pasta 'common': %s", "Failed to delete savefile.": "Falha ao excluir save.", - "Failed to delete user folder.\n%s": "Falha ao excluir pasta de usuário.\n%s", + "Failed to delete user folder: %s": "Falha ao excluir pasta de usuário: %s", "Failed to import savedata from loadiine.": "Falha ao importar save (Loadiine).", - "Failed to export savedata to loadiine.": "Falha ao exportar save (Loadiine)." + "Failed to export savedata to loadiine.": "Falha ao exportar save (Loadiine).", + "\ue083 Sort: %s \ue084": "\ue083 Organizar: %s \ue084", + "\ue000: Select BackupSet \ue001: Back": "\ue000: Selecione BackupSet \ue001: Voltar", + "\ue002: Change BackupSet \ue000: Restore \ue001: Back": "\ue002: Alterar BackupSet \ue000: Restaurar \ue001: Voltar", + "WiiU USB Savedata >> slot 0": "WiiU USB Savedata >> slot 0", + "WiiU NAND Savedata >> slot 1": "WiiU NAND Savedata >> slot 1", + "vWii Savedata >> slot 0": "vWii Savedata >> slot 0", + "BackupSet: %s": "BackupSet: %s", + ", from ": ", de ", + "Common save not restored.": "Save 'Common' não restauradoi", + "\ue001: Back": "\ue001: Voltar" } \ No newline at end of file diff --git a/languages/russian.json b/languages/russian.json index 8f4397e..5a5771b 100644 --- a/languages/russian.json +++ b/languages/russian.json @@ -72,9 +72,9 @@ "Filesize: %d bytes": "Размер: %d байт", "Deleting folder %s": "Удаление папки %s", "From: \n%s": "Из %s", - "Failed to delete folder %s\n%s": "Не удалось удалить папку %s\n%s", + "Failed to delete folder %s: %s": "Не удалось удалить папку %s: %s", "Deleting file %s": "Удаление файла %s", - "Failed to delete file %s\n%s": "Не удалось удалить файл %s\n%s", + "Failed to delete file %s: %s": "Не удалось удалить файл %s: %s", "Loadiine game folder not found.": "Папка Loadiine не найдена.", "Failed to open Loadiine game save directory.": "Не удалось открыть каталог сохранения Loadiine.", "Are you sure?": "Вы уверены?", @@ -90,9 +90,9 @@ "Restore failed.": "Ошибка восстановления.", "Hm, are you REALLY sure?": "Вы ТОЧНО уверены?", "Backup current savedata first?": "Сделать сперва бекап сохранения?", - "Failed to delete common folder.\n%s": "Не удалось удалить папку \n%s", + "Failed to delete common folder: %s": "Не удалось удалить папку: %s", "Failed to delete savefile.": "Не удалось удалить сохранение.", - "Failed to delete user folder.\n%s": "Не удалось удалить пользовательскую папку\n%s", + "Failed to delete user folder: %s": "Не удалось удалить пользовательскую папку: %s", "Failed to import savedata from loadiine.": "Не удалось импортировать сохранения из loadiine.", "Failed to export savedata to loadiine.": "Не удалось экспортировать сохранения из loadiine." } \ No newline at end of file diff --git a/languages/spanish.json b/languages/spanish.json index eb91c2b..f40efde 100644 --- a/languages/spanish.json +++ b/languages/spanish.json @@ -72,9 +72,9 @@ "Filesize: %d bytes": "Tamaño: %d bytes", "Deleting folder %s": "Eliminando carpeta %s", "From: \n%s": "Desde:\n%s", - "Failed to delete folder %s\n%s": "Error al eliminar la carpeta %s\n%s", + "Failed to delete folder %s: %s": "Error al eliminar la carpeta %s: %s", "Deleting file %s": "Eliminando archivo %s", - "Failed to delete file %s\n%s": "Error al eliminar el archivo %s\n%s", + "Failed to delete file %s: %s": "Error al eliminar el archivo %s: %s", "Loadiine game folder not found.": "Carpeta del juego de Loadiine no encontrada.", "Failed to open Loadiine game save directory.": "Error al abrir la carpeta de saves del juego de Loadiine.", "Are you sure?": "¿Estás seguro?", @@ -90,9 +90,19 @@ "Restore failed.": "Error al restaurar.", "Hm, are you REALLY sure?": "¿Hm, estás COMPLETAMENTE seguro?", "Backup current savedata first?": "¿Hacer copia antes del save?", - "Failed to delete common folder.\n%s": "Error al eliminar la carpeta common.\n%s", + "Failed to delete common folder: %s": "Error al eliminar la carpeta common: %s", "Failed to delete savefile.": "Error al eliminar el save.", - "Failed to delete user folder.\n%s": "Error al eliminar la carpeta user.\n%s", + "Failed to delete user folder: %s": "Error al eliminar la carpeta user: %s", "Failed to import savedata from loadiine.": "Error al importar el save de loadiine.", - "Failed to export savedata to loadiine.": "Error al exportar el save a loadiine." + "Failed to export savedata to loadiine.": "Error al exportar el save a loadiine.", + "\ue083 Sort: %s \ue084": "\ue083 Ordenar: %s \ue084", + "\ue000: Select BackupSet \ue001: Back": "\ue000: Seleccionar BackupSet \ue001: Atrás", + "\ue002: Change BackupSet \ue000: Restore \ue001: Back": "\ue002: Cambiar BackupSet \ue000: Restaurar \ue001: Atrás", + "WiiU USB Savedata >> slot 0": "WiiU USB Savedata >> slot 0", + "WiiU NAND Savedata >> slot 1": "WiiU NAND Savedata >> slot 1", + "vWii Savedata >> slot 0": "vWii Savedata >> slot 0", + "BackupSet: %s": "BackupSet: %s", + ", from ": ", de ", + "Common save not restored.": "No se ha podido restaurar el common save", + "\ue001: Back": "\ue001: Atrás" } \ No newline at end of file diff --git a/meta/hbl/meta.xml b/meta/hbl/meta.xml index 06de6f8..cdb49fa 100644 --- a/meta/hbl/meta.xml +++ b/meta/hbl/meta.xml @@ -1,9 +1,9 @@ SaveMii WUT - DaThinkingChair - 1.6.1 - 20220306000000 + DaThinkingChair,w3irDv + 1.6.3 + 202408240000 WiiU/vWii Save Manager WiiU/vWii Save Manager diff --git a/src/BackupSetList.cpp b/src/BackupSetList.cpp new file mode 100644 index 0000000..bdfc1ef --- /dev/null +++ b/src/BackupSetList.cpp @@ -0,0 +1,83 @@ +#include + +#include +#include +#include +#include +#include +#include + +bool BackupSetList::sortAscending = false; +const std::string BackupSetList::ROOT_BS = ">> Root <<"; +std::string BackupSetList::backupSetSubPath = "/";; +std::string BackupSetList::backupSetEntry = ROOT_BS; +std::string BackupSetList::savedBackupSetSubPath {}; + +std::unique_ptr BackupSetList::currentBackupSetList = std::make_unique(); + +extern const char *batchBackupPath; + +BackupSetList::BackupSetList(const char *backupSetListRoot) +{ + + this->backupSetListRoot = backupSetListRoot; + + backupSets.push_back(ROOT_BS); + + DIR *dir = opendir(backupSetListRoot); + if (dir != nullptr) { + struct dirent *data; + while ((data = readdir(dir)) != nullptr) { + if(strcmp(data->d_name,".") == 0 || strcmp(data->d_name,"..") == 0 || ! (data->d_type & DT_DIR)) + continue; + backupSets.push_back(data->d_name); + } + } + closedir(dir); + + this->entries = backupSets.size(); + this->sort(sortAscending); + +} + +void BackupSetList::sort(bool sortAscending_) +{ + if (sortAscending_) { + std::ranges::sort(backupSets.begin()+1,backupSets.end(),std::ranges::less{}); + } else { + std::ranges::sort(backupSets.begin()+1,backupSets.end(),std::ranges::greater{}); + } + sortAscending = sortAscending_; +} + +std::string BackupSetList::at(int i) +{ + + return backupSets.at(i); + +} + +void BackupSetList::add(std::string backupSet) +{ + backupSets.push_back(backupSet); + this->entries++; + if (!sortAscending) + this->sort(false); +} + + +void BackupSetList::initBackupSetList() { + BackupSetList::currentBackupSetList.reset(); + BackupSetList::currentBackupSetList = std::make_unique(batchBackupPath); +} + +void BackupSetList::setBackupSetEntry(int i) { + backupSetEntry = currentBackupSetList->at(i); +} + +void BackupSetList::setBackupSetSubPath() { + if (backupSetEntry == ROOT_BS) + backupSetSubPath = "/"; + else + backupSetSubPath = "/batch/"+backupSetEntry+"/"; +} diff --git a/src/Metadata.cpp b/src/Metadata.cpp new file mode 100644 index 0000000..f1adb73 --- /dev/null +++ b/src/Metadata.cpp @@ -0,0 +1,70 @@ +#include +#include + +#define FS_ALIGN(x) ((x + 0x3F) & ~(0x3F)) + +std::string Metadata::serialId { "_WIIU_" }; + +std::string Metadata::get() { + if (checkEntry(path.c_str()) != 0) { + FILE *f = fopen(path.c_str(), "rb"); + fseek(f, 0, SEEK_END); + long len = ftell(f); + fseek(f, 0, SEEK_SET); + + char *data = (char *) aligned_alloc(0x40, FS_ALIGN(len + 1)); + + fread(data, 1, len, f); + data[len] = '\0'; + fclose(f); + + json_error_t error; + json_t *root = json_loads(data, 0, &error); + + if (root) { + std::string metadata {}; + const char* date_ = json_string_value(json_object_get(root, "Date")); + if (date_ != nullptr) { + metadata.append(json_string_value(json_object_get(root, "Date"))); + } + const char* storage_ = json_string_value(json_object_get(root, "storage")); + if (storage_ != nullptr) { + metadata.append(LanguageUtils::gettext(", from ")).append(storage_); + } + const char* serialId_ = json_string_value(json_object_get(root, "serialId")); + if (serialId_ != nullptr) { + metadata.append(" | ").append(serialId_); + } + json_decref(root); + free(data); + return metadata; + } + return ""; + } + return ""; +} + +bool Metadata::set(const std::string &date, bool isUSB) { + json_t *config = json_object(); + if (config == nullptr) + return false; + + json_object_set_new(config, "Date", json_string(date.c_str())); + json_object_set_new(config, "serialId", json_string(serialId.c_str())); + json_object_set_new(config, "storage", json_string(isUSB ? "USB" : "NAND")); + + char *configString = json_dumps(config, 0); + if (configString == nullptr) + return false; + + json_decref(config); + + FILE *fp = fopen(path.c_str(), "wb"); + if (fp == nullptr) + return false; + + fwrite(configString, strlen(configString), 1, fp); + + fclose(fp); + return true; +} diff --git a/src/date.cpp b/src/date.cpp deleted file mode 100644 index ea369ea..0000000 --- a/src/date.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include - -#define FS_ALIGN(x) ((x + 0x3F) & ~(0x3F)) - -std::string Date::get() { - if (checkEntry(path.c_str()) != 0) { - FILE *f = fopen(path.c_str(), "rb"); - fseek(f, 0, SEEK_END); - long len = ftell(f); - fseek(f, 0, SEEK_SET); - - char *data = (char *) aligned_alloc(0x40, FS_ALIGN(len + 1)); - - fread(data, 1, len, f); - data[len] = '\0'; - fclose(f); - - json_error_t error; - json_t *root = json_loads(data, 0, &error); - - if (root) { - std::string buf; - buf.assign(json_string_value(json_object_get(root, "Date"))); - json_decref(root); - - free(data); - return buf; - } - return ""; - } - return ""; -} - -bool Date::set(const std::string &date) { - json_t *config = json_object(); - if (config == nullptr) - return false; - - json_object_set_new(config, "Date", json_string(date.c_str())); - - char *configString = json_dumps(config, 0); - if (configString == nullptr) - return false; - - json_decref(config); - - FILE *fp = fopen(path.c_str(), "wb"); - if (fp == nullptr) - return false; - - fwrite(configString, strlen(configString), 1, fp); - - fclose(fp); - return true; -} diff --git a/src/main.cpp b/src/main.cpp index fd3deed..59ed121 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,7 +5,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -19,6 +21,8 @@ static int wiiuTitlesCount = 0, vWiiTitlesCount = 0; +extern char *batchBackupPath; + template static bool contains(const T (&arr)[N], const T &element) { for (const auto &elem : arr) { @@ -36,6 +40,18 @@ static void disclaimer() { LanguageUtils::gettext("Everything you do with this software is your own responsibility")); } +static void getWiiUSerialId() { + // from WiiUCrashLogDumper + WUT_ALIGNAS(0x40) MCPSysProdSettings sysProd{}; + int32_t mcpHandle = MCP_Open(); + if ( mcpHandle >= 0 ) { + if (MCP_GetSysProdSettings(mcpHandle,&sysProd)==0) { + Metadata::serialId = std::string(sysProd.code_id) + sysProd.serial_id; + } + MCP_Close(mcpHandle); + } +} + static Title *loadWiiUTitles(int run) { static char *tList; static uint32_t receivedCount; @@ -386,6 +402,7 @@ static void unloadTitles(Title *titles, int count) { } int main() { + AXInit(); AXQuit(); @@ -405,6 +422,8 @@ int main() { KPADInit(); WPADEnableURCC(1); + getWiiUSerialId(); + loadWiiUTitles(0); int res = romfsInit(); @@ -435,6 +454,8 @@ int main() { sortTitle(wiiutitles, wiiutitles + wiiuTitlesCount, 1, true); sortTitle(wiititles, wiititles + vWiiTitlesCount, 1, true); + BackupSetList::initBackupSetList(); + Input input{}; std::unique_ptr state = std::make_unique(wiiutitles, wiititles, wiiuTitlesCount, vWiiTitlesCount); diff --git a/src/menu/BackupSetListState.cpp b/src/menu/BackupSetListState.cpp new file mode 100644 index 0000000..eaf2b0d --- /dev/null +++ b/src/menu/BackupSetListState.cpp @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include +#include + +#define MAX_ROWS_SHOW 14 + +static int cursorPos = 0; +static int scroll = 0; +static std::string language; + +BackupSetListState::BackupSetListState() { + this->sortAscending = BackupSetList::sortAscending; +} + +void BackupSetListState::resetCursorPosition() { // after batch Backup + if ( cursorPos == 0 ) + return; + if (BackupSetList::sortAscending) { + return; + } + else { + cursorPos++; + } +} + +void BackupSetListState::render() { + + std::string backupSetItem; + consolePrintPos(44, 0, LanguageUtils::gettext("\ue083 Sort: %s \ue084"), + this->sortAscending ? "\u2191" : "\u2193"); + for (int i = 0; i < MAX_ROWS_SHOW; i++) { + if (i + scroll < 0 || i + scroll >= BackupSetList::currentBackupSetList->entries) + break; + backupSetItem = BackupSetList::currentBackupSetList->at(i + scroll); + DrawUtils::setFontColor(COLOR_LIST); + if ( backupSetItem == BackupSetList::ROOT_BS) + DrawUtils::setFontColor(COLOR_LIST_HIGH); + if ( backupSetItem == BackupSetList::getBackupSetEntry()) + DrawUtils::setFontColor(COLOR_INFO); + consolePrintPos(M_OFF, i + 2, " %s", backupSetItem.c_str()); + } + DrawUtils::setFontColor(COLOR_TEXT); + consolePrintPos(-1, 2 + cursorPos, "\u2192"); + consolePrintPosAligned(17, 4, 2, LanguageUtils::gettext("\ue000: Select BackupSet \ue001: Back")); +} + +ApplicationState::eSubState BackupSetListState::update(Input *input) { + if (input->get(TRIGGER, PAD_BUTTON_B)) + return SUBSTATE_RETURN; + if (input->get(TRIGGER, PAD_BUTTON_A)) { + BackupSetList::setBackupSetEntry(cursorPos + scroll); + BackupSetList::setBackupSetSubPath(); + DrawUtils::setRedraw(true); + return SUBSTATE_RETURN; + } + if (input->get(TRIGGER, PAD_BUTTON_L)) { + if ( this->sortAscending ) { + this->sortAscending = false; + BackupSetList::currentBackupSetList->sort(this->sortAscending); + cursorPos = 0; + scroll = 0; + } + } + if (input->get(TRIGGER, PAD_BUTTON_R)) { + if ( ! this->sortAscending ) { + this->sortAscending = true; + BackupSetList::currentBackupSetList->sort(this->sortAscending); + cursorPos = 0; + scroll = 0; + } + } + if (input->get(TRIGGER, PAD_BUTTON_DOWN)) { + if (BackupSetList::currentBackupSetList->entries <= MAX_ROWS_SHOW) + cursorPos = (cursorPos + 1) % BackupSetList::currentBackupSetList->entries; + else if (cursorPos < 6) + cursorPos++; + else if (((cursorPos + scroll + 1) % BackupSetList::currentBackupSetList->entries) != 0) + scroll++; + else + cursorPos = scroll = 0; + } else if (input->get(TRIGGER, PAD_BUTTON_UP)) { + if (scroll > 0) + cursorPos -= (cursorPos > 6) ? 1 : 0 * (scroll--); + else if (cursorPos > 0) + cursorPos--; + else if (BackupSetList::currentBackupSetList->entries > MAX_ROWS_SHOW) + scroll = BackupSetList::currentBackupSetList->entries - (cursorPos = 6) - 1; + else + cursorPos = BackupSetList::currentBackupSetList->entries - 1; + } + return SUBSTATE_RUNNING; +} \ No newline at end of file diff --git a/src/menu/BatchBackupState.cpp b/src/menu/BatchBackupState.cpp index 9c3b66d..840a312 100644 --- a/src/menu/BatchBackupState.cpp +++ b/src/menu/BatchBackupState.cpp @@ -1,8 +1,12 @@ #include #include +#include +#include #include #include #include +#include +#include #define ENTRYCOUNT 3 @@ -29,25 +33,25 @@ ApplicationState::eSubState BatchBackupState::update(Input *input) { if (input->get(TRIGGER, PAD_BUTTON_B)) return SUBSTATE_RETURN; if (input->get(TRIGGER, PAD_BUTTON_A)) { - OSCalendarTime dateTime; + const std::string batchDatetime = getNowDateForFolder(); switch (cursorPos) { case 0: - dateTime.tm_year = 0; - backupAllSave(this->wiiutitles, this->wiiuTitlesCount, &dateTime); - backupAllSave(this->wiititles, this->vWiiTitlesCount, &dateTime); - DrawUtils::setRedraw(true); - return SUBSTATE_RETURN; + backupAllSave(this->wiiutitles, this->wiiuTitlesCount, batchDatetime); + backupAllSave(this->wiititles, this->vWiiTitlesCount, batchDatetime); + break; case 1: - backupAllSave(this->wiiutitles, this->wiiuTitlesCount, nullptr); - DrawUtils::setRedraw(true); - return SUBSTATE_RETURN; + backupAllSave(this->wiiutitles, this->wiiuTitlesCount, batchDatetime); + break; case 2: - backupAllSave(this->wiititles, this->vWiiTitlesCount, nullptr); - DrawUtils::setRedraw(true); - return SUBSTATE_RETURN; + backupAllSave(this->wiititles, this->vWiiTitlesCount, batchDatetime); + break; default: return SUBSTATE_RETURN; } + BackupSetList::initBackupSetList(); + BackupSetListState::resetCursorPosition(); + DrawUtils::setRedraw(true); + return SUBSTATE_RETURN; } return SUBSTATE_RUNNING; } \ No newline at end of file diff --git a/src/menu/ConfigMenuState.cpp b/src/menu/ConfigMenuState.cpp index 1a56616..bbd0d6a 100644 --- a/src/menu/ConfigMenuState.cpp +++ b/src/menu/ConfigMenuState.cpp @@ -11,6 +11,7 @@ void ConfigMenuState::render() { language = LanguageUtils::getLoadedLanguage(); consolePrintPos(M_OFF, 2, LanguageUtils::gettext(" Language: %s"), language.c_str()); consolePrintPos(M_OFF, 2 + cursorPos, "\u2192"); + consolePrintPosAligned(17,4,2,LanguageUtils::gettext("\ue001: Back")); } ApplicationState::eSubState ConfigMenuState::update(Input *input) { diff --git a/src/menu/TitleOptionsState.cpp b/src/menu/TitleOptionsState.cpp index f7a1850..1a2f772 100644 --- a/src/menu/TitleOptionsState.cpp +++ b/src/menu/TitleOptionsState.cpp @@ -1,367 +1,414 @@ -#include +#include +#include #include +#include +#include #include +#include #include #include void TitleOptionsState::render() { - this->isWiiUTitle = (this->title.highID == 0x00050000) || (this->title.highID == 0x00050002); - entrycount = 3; - consolePrintPos(M_OFF, 2, "[%08X-%08X] %s", this->title.highID, this->title.lowID, - this->title.shortName); + if (this->state == STATE_DO_SUBSTATE) { + if (this->subState == nullptr) { + OSFatal("SubState was null"); + } + this->subState->render(); + return; + } + if (this->state == STATE_TITLE_OPTIONS) { + if (this->task == restore) { + DrawUtils::setFontColor(COLOR_INFO); + consolePrintPosAligned(0, 4, 2,LanguageUtils::gettext("BackupSet: %s"),BackupSetList::getBackupSetEntry().c_str()); + DrawUtils::setFontColor(COLOR_TEXT); + } + this->isWiiUTitle = (this->title.highID == 0x00050000) || (this->title.highID == 0x00050002); + entrycount = 3; + consolePrintPos(M_OFF, 2, "[%08X-%08X] %s", this->title.highID, this->title.lowID, + this->title.shortName); + if (this->task == copytoOtherDevice) { + consolePrintPos(M_OFF, 4, LanguageUtils::gettext("Destination:")); + consolePrintPos(M_OFF, 5, " (%s)", this->title.isTitleOnUSB ? "NAND" : "USB"); + } else if (this->task > 2) { + entrycount = 2; + consolePrintPos(M_OFF, 4, LanguageUtils::gettext("Select %s:"), LanguageUtils::gettext("version")); + consolePrintPos(M_OFF, 5, " < v%u >", this->versionList != nullptr ? this->versionList[slot] : 0); + } else if (this->task == wipe) { + consolePrintPos(M_OFF, 4, LanguageUtils::gettext("Delete from:")); + consolePrintPos(M_OFF, 5, " (%s)", this->title.isTitleOnUSB ? "USB" : "NAND"); + } else { + consolePrintPos(M_OFF, 4, LanguageUtils::gettext("Select %s:"), LanguageUtils::gettext("slot")); - if (this->task == copytoOtherDevice) { - consolePrintPos(M_OFF, 4, LanguageUtils::gettext("Destination:")); - consolePrintPos(M_OFF, 5, " (%s)", this->title.isTitleOnUSB ? "NAND" : "USB"); - } else if (this->task > 2) { - entrycount = 2; - consolePrintPos(M_OFF, 4, LanguageUtils::gettext("Select %s:"), LanguageUtils::gettext("version")); - consolePrintPos(M_OFF, 5, " < v%u >", this->versionList != nullptr ? this->versionList[slot] : 0); - } else if (this->task == wipe) { - consolePrintPos(M_OFF, 4, LanguageUtils::gettext("Delete from:")); - consolePrintPos(M_OFF, 5, " (%s)", this->title.isTitleOnUSB ? "USB" : "NAND"); - } else { - consolePrintPos(M_OFF, 4, LanguageUtils::gettext("Select %s:"), LanguageUtils::gettext("slot")); + if (((this->title.highID & 0xFFFFFFF0) == 0x00010000) && (slot == 255)) + consolePrintPos(M_OFF, 5, " < SaveGame Manager GX > (%s)", + isSlotEmpty(this->title.highID, this->title.lowID, slot) ? LanguageUtils::gettext("Empty") + : LanguageUtils::gettext("Used")); + else + consolePrintPos(M_OFF, 5, " < %03u > (%s)", slot, + isSlotEmpty(this->title.highID, this->title.lowID, slot) ? LanguageUtils::gettext("Empty") + : LanguageUtils::gettext("Used")); + } - if (((this->title.highID & 0xFFFFFFF0) == 0x00010000) && (slot == 255)) - consolePrintPos(M_OFF, 5, " < SaveGame Manager GX > (%s)", - isSlotEmpty(this->title.highID, this->title.lowID, slot) ? LanguageUtils::gettext("Empty") - : LanguageUtils::gettext("Used")); - else - consolePrintPos(M_OFF, 5, " < %03u > (%s)", slot, - isSlotEmpty(this->title.highID, this->title.lowID, slot) ? LanguageUtils::gettext("Empty") - : LanguageUtils::gettext("Used")); - } + if (this->isWiiUTitle) { + if (task == restore) { + if (!isSlotEmpty(this->title.highID, this->title.lowID, slot)) { + entrycount++; + consolePrintPos(M_OFF, 7, LanguageUtils::gettext("Select SD user to copy from:")); + if (sduser == -1) + consolePrintPos(M_OFF, 8, " < %s >", LanguageUtils::gettext("all users")); + else + consolePrintPos(M_OFF, 8, " < %s > (%s)", getSDacc()[sduser].persistentID, + hasAccountSave(&this->title, true, false, getSDacc()[sduser].pID, + slot, 0) + ? LanguageUtils::gettext("Has Save") + : LanguageUtils::gettext("Empty")); + } + } - if (this->isWiiUTitle) { - if (task == restore) { - if (!isSlotEmpty(this->title.highID, this->title.lowID, slot)) { - entrycount++; - consolePrintPos(M_OFF, 7, LanguageUtils::gettext("Select SD user to copy from:")); - if (sdusers == -1) + if (task == wipe) { + consolePrintPos(M_OFF, 7, LanguageUtils::gettext("Select Wii U user to delete from:")); + if (this->wiiuuser == -1) consolePrintPos(M_OFF, 8, " < %s >", LanguageUtils::gettext("all users")); else - consolePrintPos(M_OFF, 8, " < %s > (%s)", getSDacc()[sdusers].persistentID, - hasAccountSave(&this->title, true, false, getSDacc()[sdusers].pID, - slot, 0) + consolePrintPos(M_OFF, 8, " < %s (%s) > (%s)", getWiiUacc()[this->wiiuuser].miiName, + getWiiUacc()[this->wiiuuser].persistentID, + hasAccountSave(&this->title, false, false, getWiiUacc()[this->wiiuuser].pID, + slot, 0) ? LanguageUtils::gettext("Has Save") : LanguageUtils::gettext("Empty")); } - } - if (task == wipe) { - consolePrintPos(M_OFF, 7, LanguageUtils::gettext("Select Wii U user to delete from:")); - if (this->allusers == -1) - consolePrintPos(M_OFF, 8, " < %s >", LanguageUtils::gettext("all users")); - else - consolePrintPos(M_OFF, 8, " < %s (%s) > (%s)", getWiiUacc()[this->allusers].miiName, - getWiiUacc()[this->allusers].persistentID, - hasAccountSave(&this->title, false, false, getWiiUacc()[this->allusers].pID, - slot, 0) - ? LanguageUtils::gettext("Has Save") - : LanguageUtils::gettext("Empty")); - } + if ((task == backup) || (task == restore) || (task == copytoOtherDevice)) { + if ((task == restore) && isSlotEmpty(this->title.highID, this->title.lowID, slot)) + entrycount--; + else { + consolePrintPos(M_OFF, (task == restore) ? 10 : 7, LanguageUtils::gettext("Select Wii U user%s:"), + (task == copytoOtherDevice) ? LanguageUtils::gettext(" to copy from") : ((task == restore) ? LanguageUtils::gettext(" to copy to") : "")); + if (this->wiiuuser == -1) + consolePrintPos(M_OFF, (task == restore) ? 11 : 8, " < %s >", LanguageUtils::gettext("all users")); + else + consolePrintPos(M_OFF, (task == restore) ? 11 : 8, " < %s (%s) > (%s)", + getWiiUacc()[wiiuuser].miiName, getWiiUacc()[wiiuuser].persistentID, + hasAccountSave(&this->title, + (!((task == backup) || (task == restore) || (task == copytoOtherDevice))), + (!((task < 3) || (task == copytoOtherDevice))), + getWiiUacc()[this->wiiuuser].pID, slot, + this->versionList != nullptr ? this->versionList[slot] : 0) + ? LanguageUtils::gettext("Has Save") + : LanguageUtils::gettext("Empty")); + } + } + if ((task == backup) || (task == restore)) + if (!isSlotEmpty(this->title.highID, this->title.lowID, slot)) { + Metadata *metadataObj = new Metadata(this->title.highID, this->title.lowID, slot); + consolePrintPos(M_OFF, 15, LanguageUtils::gettext("Date: %s"), + metadataObj->get().c_str()); + delete metadataObj; + } - if ((task == backup) || (task == restore) || (task == copytoOtherDevice)) { - if ((task == restore) && isSlotEmpty(this->title.highID, this->title.lowID, slot)) - entrycount--; - else { - consolePrintPos(M_OFF, (task == restore) ? 10 : 7, LanguageUtils::gettext("Select Wii U user%s:"), - (task == copytoOtherDevice) ? LanguageUtils::gettext(" to copy from") : ((task == restore) ? LanguageUtils::gettext(" to copy to") : "")); - if (this->allusers == -1) - consolePrintPos(M_OFF, (task == restore) ? 11 : 8, " < %s >", LanguageUtils::gettext("all users")); + if (task == copytoOtherDevice) { + entrycount++; + consolePrintPos(M_OFF, 10, LanguageUtils::gettext("Select Wii U user%s:"), (task == copytoOtherDevice) ? LanguageUtils::gettext(" to copy to") : ""); + if (wiiuuser_d == -1) + consolePrintPos(M_OFF, 11, " < %s >", LanguageUtils::gettext("all users")); else - consolePrintPos(M_OFF, (task == restore) ? 11 : 8, " < %s (%s) > (%s)", - getWiiUacc()[allusers].miiName, getWiiUacc()[allusers].persistentID, - hasAccountSave(&this->title, - (!((task == backup) || (task == restore) || (task == copytoOtherDevice))), - (!((task < 3) || (task == copytoOtherDevice))), - getWiiUacc()[this->allusers].pID, slot, - this->versionList != nullptr ? this->versionList[slot] : 0) + consolePrintPos(M_OFF, 11, " < %s (%s) > (%s)", getWiiUacc()[wiiuuser_d].miiName, + getWiiUacc()[wiiuuser_d].persistentID, + hasAccountSave(&titles[this->title.dupeID], false, false, + getWiiUacc()[wiiuuser_d].pID, 0, 0) ? LanguageUtils::gettext("Has Save") : LanguageUtils::gettext("Empty")); } - } - if ((task == backup) || (task == restore)) - if (!isSlotEmpty(this->title.highID, this->title.lowID, slot)) { - Date *dateObj = new Date(this->title.highID, this->title.lowID, slot); - consolePrintPos(M_OFF, 15, LanguageUtils::gettext("Date: %s"), - dateObj->get().c_str()); - delete dateObj; - } - - if (task == copytoOtherDevice) { - entrycount++; - consolePrintPos(M_OFF, 10, LanguageUtils::gettext("Select Wii U user%s:"), (task == copytoOtherDevice) ? LanguageUtils::gettext(" to copy to") : ""); - if (allusers_d == -1) - consolePrintPos(M_OFF, 11, " < %s >", LanguageUtils::gettext("all users")); - else - consolePrintPos(M_OFF, 11, " < %s (%s) > (%s)", getWiiUacc()[allusers_d].miiName, - getWiiUacc()[allusers_d].persistentID, - hasAccountSave(&titles[this->title.dupeID], false, false, - getWiiUacc()[allusers_d].pID, 0, 0) - ? LanguageUtils::gettext("Has Save") - : LanguageUtils::gettext("Empty")); - } - if ((task != importLoadiine) && (task != exportLoadiine)) { - if (this->allusers > -1) { - if (hasCommonSave(&this->title, - (!((task == backup) || (task == wipe) || (task == copytoOtherDevice))), - (!((task < 3) || (task == copytoOtherDevice))), slot, - this->versionList != nullptr ? this->versionList[slot] : 0)) { - consolePrintPos(M_OFF, (task == restore) || (task == copytoOtherDevice) ? 13 : 10, - LanguageUtils::gettext("Include 'common' save?")); - consolePrintPos(M_OFF, (task == restore) || (task == copytoOtherDevice) ? 14 : 11, " < %s >", - common ? LanguageUtils::gettext("yes") : LanguageUtils::gettext("no ")); + if ((task != importLoadiine) && (task != exportLoadiine)) { + if (this->wiiuuser > -1) { + if (hasCommonSave(&this->title, + (!((task == backup) || (task == wipe) || (task == copytoOtherDevice))), + (!((task < 3) || (task == copytoOtherDevice))), slot, + this->versionList != nullptr ? this->versionList[slot] : 0)) { + consolePrintPos(M_OFF, (task == restore) || (task == copytoOtherDevice) ? 13 : 10, + LanguageUtils::gettext("Include 'common' save?")); + consolePrintPos(M_OFF, (task == restore) || (task == copytoOtherDevice) ? 14 : 11, " < %s >", + common ? LanguageUtils::gettext("yes") : LanguageUtils::gettext("no ")); + } else { + common = false; + consolePrintPos(M_OFF, (task == restore) || (task == copytoOtherDevice) ? 13 : 10, + LanguageUtils::gettext("No 'common' save found.")); + entrycount--; + } } else { common = false; - consolePrintPos(M_OFF, (task == restore) || (task == copytoOtherDevice) ? 13 : 10, - LanguageUtils::gettext("No 'common' save found.")); entrycount--; } } else { - common = false; - entrycount--; + if (hasCommonSave(&this->title, true, true, slot, this->versionList != nullptr ? this->versionList[slot] : 0)) { + consolePrintPos(M_OFF, 7, LanguageUtils::gettext("Include 'common' save?")); + consolePrintPos(M_OFF, 8, " < %s >", common ? LanguageUtils::gettext("yes") : LanguageUtils::gettext("no ")); + } else { + common = false; + consolePrintPos(M_OFF, 7, LanguageUtils::gettext("No 'common' save found.")); + entrycount--; + } } + + consolePrintPos(M_OFF, 5 + cursorPos * 3, "\u2192"); + if (this->title.iconBuf != nullptr) + DrawUtils::drawTGA(660, 100, 1, this->title.iconBuf); } else { - if (hasCommonSave(&this->title, true, true, slot, this->versionList != nullptr ? this->versionList[slot] : 0)) { - consolePrintPos(M_OFF, 7, LanguageUtils::gettext("Include 'common' save?")); - consolePrintPos(M_OFF, 8, " < %s >", common ? LanguageUtils::gettext("yes") : LanguageUtils::gettext("no ")); - } else { - common = false; - consolePrintPos(M_OFF, 7, LanguageUtils::gettext("No 'common' save found.")); - entrycount--; + entrycount = 1; + if (this->title.iconBuf != nullptr) + DrawUtils::drawRGB5A3(650, 100, 1, this->title.iconBuf); + if (!isSlotEmpty(this->title.highID, this->title.lowID, slot)) { + Metadata *metadataObj = new Metadata(this->title.highID, this->title.lowID, slot); + consolePrintPos(M_OFF, 15, LanguageUtils::gettext("Date: %s"), + metadataObj->get().c_str()); + delete metadataObj; } } - consolePrintPos(M_OFF, 5 + cursorPos * 3, "\u2192"); - if (this->title.iconBuf != nullptr) - DrawUtils::drawTGA(660, 100, 1, this->title.iconBuf); - } else { - entrycount = 1; - if (this->title.iconBuf != nullptr) - DrawUtils::drawRGB5A3(650, 100, 1, this->title.iconBuf); - if (!isSlotEmpty(this->title.highID, this->title.lowID, slot)) { - Date *dateObj = new Date(this->title.highID, this->title.lowID, slot); - consolePrintPos(M_OFF, 15, LanguageUtils::gettext("Date: %s"), - dateObj->get().c_str()); - delete dateObj; + switch (task) { + case backup: + consolePrintPosAligned(17, 4, 2, LanguageUtils::gettext("\ue000: Backup \ue001: Back")); + break; + case restore: + consolePrintPosAligned(17, 4, 2, LanguageUtils::gettext("\uE002: Change BackupSet \ue000: Restore \ue001: Back")); + break; + case wipe: + consolePrintPosAligned(17, 4, 2, LanguageUtils::gettext("\ue000: Wipe \ue001: Back")); + break; + case importLoadiine: + consolePrintPosAligned(17, 4, 2, LanguageUtils::gettext("\ue000: Import \ue001: Back")); + break; + case exportLoadiine: + consolePrintPosAligned(17, 4, 2, LanguageUtils::gettext("\ue000: Export \ue001: Back")); + break; + case copytoOtherDevice: + consolePrintPosAligned(17, 4, 2, LanguageUtils::gettext("\ue000: Copy \ue001: Back")); + break; } } - - switch (task) { - case backup: - consolePrintPosAligned(17, 4, 2, LanguageUtils::gettext("\ue000: Backup \ue001: Back")); - break; - case restore: - consolePrintPosAligned(17, 4, 2, LanguageUtils::gettext("\ue000: Restore \ue001: Back")); - break; - case wipe: - consolePrintPosAligned(17, 4, 2, LanguageUtils::gettext("\ue000: Wipe \ue001: Back")); - break; - case importLoadiine: - consolePrintPosAligned(17, 4, 2, LanguageUtils::gettext("\ue000: Import \ue001: Back")); - break; - case exportLoadiine: - consolePrintPosAligned(17, 4, 2, LanguageUtils::gettext("\ue000: Export \ue001: Back")); - break; - case copytoOtherDevice: - consolePrintPosAligned(17, 4, 2, LanguageUtils::gettext("\ue000: Copy \ue001: Back")); - break; - } } ApplicationState::eSubState TitleOptionsState::update(Input *input) { - if (input->get(TRIGGER, PAD_BUTTON_B)) - return SUBSTATE_RETURN; - if (input->get(TRIGGER, PAD_BUTTON_LEFT)) { - if (this->task == copytoOtherDevice) { - switch (cursorPos) { - case 0: - break; - case 1: - this->allusers = ((this->allusers == -1) ? -1 : (this->allusers - 1)); - allusers_d = this->allusers; - break; - case 2: - allusers_d = (((this->allusers == -1) || (allusers_d == -1)) ? -1 : (allusers_d - 1)); - allusers_d = ((this->allusers > -1) && (allusers_d == -1)) ? 0 : allusers_d; - break; - case 3: - common = common ? false : true; - break; - default: - break; - } - } else if (this->task == restore) { - switch (cursorPos) { - case 0: - getAccountsSD(&this->title, --slot); - break; - case 1: - sdusers = ((sdusers == -1) ? -1 : (sdusers - 1)); - this->allusers = ((sdusers == -1) ? -1 : this->allusers); - break; - case 2: - allusers = (((allusers == -1) || (sdusers == -1)) ? -1 : (allusers - 1)); - allusers = ((sdusers > -1) && (allusers == -1)) ? 0 : allusers; - break; - case 3: - common = common ? false : true; - break; - default: - break; - } - } else if (this->task == wipe) { - switch (cursorPos) { - case 0: - break; - case 1: - allusers = ((allusers == -1) ? -1 : (allusers - 1)); - break; - case 2: - common = common ? false : true; - break; - default: - break; - } - } else if ((this->task == importLoadiine) || (this->task == exportLoadiine)) { - switch (cursorPos) { - case 0: - slot--; - break; - case 1: - common = common ? false : true; - break; - default: - break; - } - } else { - switch (cursorPos) { - case 0: - slot--; - break; - case 1: - allusers = ((allusers == -1) ? -1 : (allusers - 1)); - break; - case 2: - common = common ? false : true; - break; - default: - break; - } + if (this->state == STATE_TITLE_OPTIONS) { + if (input->get(TRIGGER, PAD_BUTTON_B)) { + BackupSetList::setBackupSetSubPathToRoot(); // reset nxt operations to current backupSet + return SUBSTATE_RETURN; } - } - if (input->get(TRIGGER, PAD_BUTTON_RIGHT)) { - if (this->task == copytoOtherDevice) { - switch (cursorPos) { - case 0: - break; - case 1: - allusers = ((allusers == (getWiiUaccn() - 1)) ? (getWiiUaccn() - 1) : (allusers + 1)); - allusers_d = allusers; - break; - case 2: - allusers_d = ((allusers_d == (getWiiUaccn() - 1)) ? (getWiiUaccn() - 1) : (allusers_d + 1)); - allusers_d = (allusers == -1) ? -1 : allusers_d; - break; - case 3: - common = common ? false : true; - break; - default: - break; - } - } else if (this->task == restore) { - switch (cursorPos) { - case 0: - getAccountsSD(&this->title, ++slot); - break; - case 1: - sdusers = ((sdusers == (getSDaccn() - 1)) ? (getSDaccn() - 1) : (sdusers + 1)); - allusers = ((sdusers > -1) && (allusers == -1)) ? 0 : allusers; - break; - case 2: - allusers = ((allusers == (getWiiUaccn() - 1)) ? (getWiiUaccn() - 1) : (allusers + 1)); - allusers = (sdusers == -1) ? -1 : allusers; - break; - case 3: - common = common ? false : true; - break; - default: - break; + if (input->get(TRIGGER, PAD_BUTTON_X)) + if (this->task == restore) { + this->state = STATE_DO_SUBSTATE; + this->subState = std::make_unique(); } - } else if (this->task == wipe) { - switch (cursorPos) { - case 0: - break; - case 1: - allusers = ((allusers == (getWiiUaccn() - 1)) ? (getWiiUaccn() - 1) : (allusers + 1)); - break; - case 2: - common = common ? false : true; - break; - default: - break; + if (input->get(TRIGGER, PAD_BUTTON_LEFT)) { + if (this->task == copytoOtherDevice) { + switch (cursorPos) { + case 0: + break; + case 1: + this->wiiuuser = ((this->wiiuuser == -1) ? -1 : (this->wiiuuser - 1)); + wiiuuser_d = this->wiiuuser; + break; + case 2: + wiiuuser_d = (((this->wiiuuser == -1) || (wiiuuser_d == -1)) ? -1 : (wiiuuser_d - 1)); + wiiuuser_d = ((this->wiiuuser > -1) && (wiiuuser_d == -1)) ? 0 : wiiuuser_d; + break; + case 3: + common = common ? false : true; + break; + default: + break; + } + } else if (this->task == restore) { + switch (cursorPos) { + case 0: + getAccountsSD(&this->title, --slot); + if ( sduser > getSDaccn() - 1 ) + { + sduser = -1; + wiiuuser = -1; + } + break; + case 1: + sduser = ((sduser == -1) ? -1 : (sduser - 1)); + this->wiiuuser = ((sduser == -1) ? -1 : this->wiiuuser); + break; + case 2: + wiiuuser = (((wiiuuser == -1) || (sduser == -1)) ? -1 : (wiiuuser - 1)); + wiiuuser = ((sduser > -1) && (wiiuuser == -1)) ? 0 : wiiuuser; + break; + case 3: + common = common ? false : true; + break; + default: + break; + } + } else if (this->task == wipe) { + switch (cursorPos) { + case 0: + break; + case 1: + wiiuuser = ((wiiuuser == -1) ? -1 : (wiiuuser - 1)); + break; + case 2: + common = common ? false : true; + break; + default: + break; + } + } else if ((this->task == importLoadiine) || (this->task == exportLoadiine)) { + switch (cursorPos) { + case 0: + slot--; + break; + case 1: + common = common ? false : true; + break; + default: + break; + } + } else { + switch (cursorPos) { + case 0: + slot--; + break; + case 1: + wiiuuser = ((wiiuuser == -1) ? -1 : (wiiuuser - 1)); + break; + case 2: + common = common ? false : true; + break; + default: + break; + } } - } else if ((this->task == importLoadiine) || (this->task == exportLoadiine)) { - switch (cursorPos) { - case 0: - slot++; - break; - case 1: - common = common ? false : true; - break; - default: - break; + } + if (input->get(TRIGGER, PAD_BUTTON_RIGHT)) { + if (this->task == copytoOtherDevice) { + switch (cursorPos) { + case 0: + break; + case 1: + wiiuuser = ((wiiuuser == (getWiiUaccn() - 1)) ? (getWiiUaccn() - 1) : (wiiuuser + 1)); + wiiuuser_d = wiiuuser; + break; + case 2: + wiiuuser_d = ((wiiuuser_d == (getWiiUaccn() - 1)) ? (getWiiUaccn() - 1) : (wiiuuser_d + 1)); + wiiuuser_d = (wiiuuser == -1) ? -1 : wiiuuser_d; + break; + case 3: + common = common ? false : true; + break; + default: + break; + } + } else if (this->task == restore) { + switch (cursorPos) { + case 0: + getAccountsSD(&this->title, ++slot); + if ( sduser > getSDaccn() -1 ) + { + sduser = -1; + wiiuuser = -1; + } + break; + case 1: + sduser = ((sduser == (getSDaccn() - 1)) ? (getSDaccn() - 1) : (sduser + 1)); + wiiuuser = ((sduser > -1) && (wiiuuser == -1)) ? 0 : wiiuuser; + break; + case 2: + wiiuuser = ((wiiuuser == (getWiiUaccn() - 1)) ? (getWiiUaccn() - 1) : (wiiuuser + 1)); + wiiuuser = (sduser == -1) ? -1 : wiiuuser; + break; + case 3: + common = common ? false : true; + break; + default: + break; + } + } else if (this->task == wipe) { + switch (cursorPos) { + case 0: + break; + case 1: + wiiuuser = ((wiiuuser == (getWiiUaccn() - 1)) ? (getWiiUaccn() - 1) : (wiiuuser + 1)); + break; + case 2: + common = common ? false : true; + break; + default: + break; + } + } else if ((this->task == importLoadiine) || (this->task == exportLoadiine)) { + switch (cursorPos) { + case 0: + slot++; + break; + case 1: + common = common ? false : true; + break; + default: + break; + } + } else { + switch (cursorPos) { + case 0: + slot++; + break; + case 1: + wiiuuser = ((wiiuuser == (getWiiUaccn() - 1)) ? (getWiiUaccn() - 1) : (wiiuuser + 1)); + break; + case 2: + common = common ? false : true; + break; + default: + break; + } } - } else { - switch (cursorPos) { - case 0: - slot++; - break; - case 1: - allusers = ((allusers == (getWiiUaccn() - 1)) ? (getWiiUaccn() - 1) : (allusers + 1)); - break; - case 2: - common = common ? false : true; - break; - default: + } + if (input->get(TRIGGER, PAD_BUTTON_DOWN)) { + if (entrycount <= 14) + cursorPos = (cursorPos + 1) % entrycount; + } else if (input->get(TRIGGER, PAD_BUTTON_UP)) { + if (cursorPos > 0) + --cursorPos; + } + if (input->get(TRIGGER, PAD_BUTTON_A)) { + switch (this->task) { + case backup: + backupSavedata(&this->title, slot, wiiuuser, common); + DrawUtils::setRedraw(true); + break; + case restore: + restoreSavedata(&this->title, slot, sduser, wiiuuser, common); + DrawUtils::setRedraw(true); + break; + case wipe: + wipeSavedata(&this->title, wiiuuser, common); + DrawUtils::setRedraw(true); + break; + case copytoOtherDevice: + for (int i = 0; i < this->titleCount; i++) { + if (titles[i].listID == this->title.dupeID) { + copySavedata(&this->title, &titles[i], wiiuuser, wiiuuser_d, common); + DrawUtils::setRedraw(true); + break; + } + } + DrawUtils::setRedraw(true); break; } } - } - if (input->get(TRIGGER, PAD_BUTTON_DOWN)) { - if (entrycount <= 14) - cursorPos = (cursorPos + 1) % entrycount; - } else if (input->get(TRIGGER, PAD_BUTTON_UP)) { - if (cursorPos > 0) - --cursorPos; - } - if (input->get(TRIGGER, PAD_BUTTON_A)) { - switch (this->task) { - case backup: - backupSavedata(&this->title, slot, allusers, common); - DrawUtils::setRedraw(true); - break; - case restore: - restoreSavedata(&this->title, slot, sdusers, allusers, common); - DrawUtils::setRedraw(true); - break; - case wipe: - wipeSavedata(&this->title, allusers, common); - DrawUtils::setRedraw(true); - break; - case copytoOtherDevice: - for (int i = 0; i < this->titleCount; i++) { - if (titles[i].listID == this->title.dupeID) { - copySavedata(&this->title, &titles[i], allusers, allusers_d, common); - DrawUtils::setRedraw(true); - break; - } - } - DrawUtils::setRedraw(true); - break; + } else if (this->state == STATE_DO_SUBSTATE) { + auto retSubState = this->subState->update(input); + if (retSubState == SUBSTATE_RUNNING) { + // keep running. + return SUBSTATE_RUNNING; + } else if (retSubState == SUBSTATE_RETURN) { + this->subState.reset(); + this->state = STATE_TITLE_OPTIONS; + slot = 0; + getAccountsSD(&this->title, slot); } } return SUBSTATE_RUNNING; diff --git a/src/menu/TitleTaskState.cpp b/src/menu/TitleTaskState.cpp index 886be10..91f26b7 100644 --- a/src/menu/TitleTaskState.cpp +++ b/src/menu/TitleTaskState.cpp @@ -2,14 +2,17 @@ #include #include #include +#include +#include #include #include #include +#include static int cursorPos = 0; static int entrycount; static uint8_t slot = 0; -static int8_t allusers = -1, allusers_d = -1, sdusers = -1; +static int8_t wiiuuser = -1, wiiuuser_d = -1, sduser = -1; static bool common = true; void TitleTaskState::render() { @@ -21,6 +24,9 @@ void TitleTaskState::render() { return; } if (this->state == STATE_TITLE_TASKS) { + DrawUtils::setFontColor(COLOR_INFO); + consolePrintPosAligned(0, 4, 2,LanguageUtils::gettext("WiiU Serial Id: %s"),Metadata::serialId.c_str()); + DrawUtils::setFontColor(COLOR_TEXT); this->isWiiUTitle = (this->title.highID == 0x00050000) || (this->title.highID == 0x00050002); entrycount = 3 + 2 * static_cast(this->isWiiUTitle) + 1 * static_cast(this->isWiiUTitle && (this->title.isTitleDupe)); consolePrintPos(M_OFF, 2, " [%08X-%08X] [%s]", this->title.highID, this->title.lowID, @@ -61,9 +67,10 @@ ApplicationState::eSubState TitleTaskState::update(Input *input) { } if (this->task == restore) { + BackupSetList::setBackupSetSubPath(); getAccountsSD(&this->title, slot); - allusers = ((sdusers == -1) ? -1 : allusers); - sdusers = ((allusers == -1) ? -1 : sdusers); + wiiuuser = ((sduser == -1) ? -1 : wiiuuser); + sduser = ((wiiuuser == -1) ? -1 : sduser); } if (this->task == wipe) { @@ -101,7 +108,7 @@ ApplicationState::eSubState TitleTaskState::update(Input *input) { if (noError) { DrawUtils::setRedraw(true); this->state = STATE_DO_SUBSTATE; - this->subState = std::make_unique(this->title, this->task, this->versionList, sdusers, allusers, common, allusers_d, this->titles, this->titlesCount); + this->subState = std::make_unique(this->title, this->task, this->versionList, sduser, wiiuuser, common, wiiuuser_d, this->titles, this->titlesCount); } } if (cursorPos > entrycount) diff --git a/src/savemng.cpp b/src/savemng.cpp index e7bafc7..186e30b 100644 --- a/src/savemng.cpp +++ b/src/savemng.cpp @@ -1,21 +1,22 @@ +#include +#include #include +#include +#include +#include +#include #include #include -#include #include #include -#include #include -#include -#include -#include #include - #define __FSAShimSend ((FSError(*)(FSAShimBuffer *, uint32_t))(0x101C400 + 0x042d90)) #define IO_MAX_FILE_BUFFER (1024 * 1024) // 1 MB const char *backupPath = "fs:/vol/external01/wiiu/backups"; +const char *batchBackupPath = "fs:/vol/external01/wiiu/backups/batch"; // Must be "backupPath/batch" ~ backupSetListRoot const char *loadiineSavePath = "fs:/vol/external01/wiiu/saves"; const char *legacyBackupPath = "fs:/vol/external01/savegames"; @@ -53,12 +54,16 @@ std::string newlibtoFSA(std::string path) { return path; } -std::string getBackupPath(uint32_t highId, uint32_t lowId, uint8_t slot){ - return StringUtils::stringFormat("%s/%08x%08x/%u", backupPath, highId, lowId, slot); +std::string getDynamicBackupPath(uint32_t highID, uint32_t lowID, uint8_t slot) { + + if (((highID & 0xFFFFFFF0) == 0x00010000) && (slot == 255)) + return StringUtils::stringFormat("%s/%08x%08x", legacyBackupPath, highID, lowID); // LegacyBackup + else + return StringUtils::stringFormat("%s%s%08x%08x/%u", backupPath, BackupSetList::getBackupSetSubPath().c_str(), highID, lowID, slot); } -std::string getLegacyBackupPath(uint32_t highId, uint32_t lowId){ - return StringUtils::stringFormat("%s/%08x%08x", legacyBackupPath, highId, lowId); +std::string getBatchBackupPath(uint32_t highID, uint32_t lowID, uint8_t slot, std::string datetime) { + return StringUtils::stringFormat("%s/%s/%08x%08x/%u", batchBackupPath, datetime.c_str(),highID, lowID, slot); } uint8_t getSDaccn() { @@ -394,7 +399,7 @@ void promptError(const char *message, ...) { free(tmp); DrawUtils::endDraw(); va_end(va); - sleep(2); + sleep(4); } void getAccountsWiiU() { @@ -409,7 +414,7 @@ void getAccountsWiiU() { if (nn::act::IsSlotOccupied(i)) { unsigned int persistentID = nn::act::GetPersistentIdEx(i); wiiuacc[accn].pID = persistentID; - sprintf(wiiuacc[accn].persistentID, "%08X", persistentID); + sprintf(wiiuacc[accn].persistentID, "%08x", persistentID); nn::act::GetMiiNameEx((int16_t *) out, i); memset(wiiuacc[accn].miiName, 0, sizeof(wiiuacc[accn].miiName)); for (int j = 0, k = 0; j < 10; j++) { @@ -442,7 +447,7 @@ void getAccountsSD(Title *title, uint8_t slot) { if (sdacc != nullptr) free(sdacc); - std::string path = getBackupPath(highID, lowID, slot); + std::string path = getDynamicBackupPath(highID, lowID, slot); DIR *dir = opendir(path.c_str()); if (dir != nullptr) { struct dirent *data; @@ -612,7 +617,7 @@ static bool removeDir(const std::string &pPath) { return false; struct dirent *data; - + while ((data = readdir(dir)) != nullptr) { DrawUtils::beginDraw(); DrawUtils::clear(COLOR_BLACK); @@ -630,11 +635,11 @@ static bool removeDir(const std::string &pPath) { consolePrintPos(-2, 0, LanguageUtils::gettext("Deleting folder %s"), data->d_name); consolePrintPosMultiline(-2, 2, LanguageUtils::gettext("From: \n%s"), origPath.c_str()); - if (unlink(origPath.c_str()) == -1) promptError(LanguageUtils::gettext("Failed to delete folder %s\n%s"), origPath.c_str(), strerror(errno)); + if (unlink(origPath.c_str()) == -1) promptError(LanguageUtils::gettext("Failed to delete folder %s: %s"), origPath.c_str(), strerror(errno)); } else { consolePrintPos(-2, 0, LanguageUtils::gettext("Deleting file %s"), data->d_name); consolePrintPosMultiline(-2, 2, LanguageUtils::gettext("From: \n%s"), tempPath.c_str()); - if (unlink(tempPath.c_str()) == -1) promptError(LanguageUtils::gettext("Failed to delete file %s\n%s"), tempPath.c_str(), strerror(errno)); + if (unlink(tempPath.c_str()) == -1) promptError(LanguageUtils::gettext("Failed to delete file %s: %s"), tempPath.c_str(), strerror(errno)); } DrawUtils::endDraw(); @@ -719,10 +724,14 @@ static bool getLoadiineUserDir(char *out, const char *fullSavePath, const char * bool isSlotEmpty(uint32_t highID, uint32_t lowID, uint8_t slot) { std::string path; - if (((highID & 0xFFFFFFF0) == 0x00010000) && (slot == 255)) - path = getLegacyBackupPath(highID, lowID); - else - path = getBackupPath(highID, lowID, slot); + path = getDynamicBackupPath(highID, lowID,slot); + int ret = checkEntry(path.c_str()); + return ret <= 0; +} + +bool isSlotEmpty(uint32_t highID, uint32_t lowID, uint8_t slot, const std::string &batchDatetime) { + std::string path; + path = getBatchBackupPath(highID,lowID,slot,batchDatetime); int ret = checkEntry(path.c_str()); return ret <= 0; } @@ -734,6 +743,14 @@ static int getEmptySlot(uint32_t highID, uint32_t lowID) { return -1; } +static int getEmptySlot(uint32_t highID, uint32_t lowID, const std::string &batchDatetime) { + for (int i = 0; i < 256; i++) + if (isSlotEmpty(highID, lowID, i, batchDatetime)) + return i; + return -1; +} + + bool hasAccountSave(Title *title, bool inSD, bool iine, uint32_t user, uint8_t slot, int version) { uint32_t highID = title->highID; uint32_t lowID = title->lowID; @@ -752,11 +769,11 @@ bool hasAccountSave(Title *title, bool inSD, bool iine, uint32_t user, uint8_t s } else if (user == 0xFFFFFFFF) { sprintf(srcPath, "%s/%08x/%08x/%s", path, highID, lowID, "user"); } else { - sprintf(srcPath, "%s/%08x/%08x/%s/%08X", path, highID, lowID, "user", user); + sprintf(srcPath, "%s/%08x/%08x/%s/%08x", path, highID, lowID, "user", user); } } else { if (!iine) { - sprintf(srcPath, "%s/%08X", getBackupPath(highID, lowID, slot).c_str(), user); + sprintf(srcPath, "%s/%08x", getDynamicBackupPath(highID, lowID, slot).c_str(), user); } else { if (!getLoadiineGameSaveDir(srcPath, title->productCode, title->longName, title->highID, title->lowID)) { return false; @@ -778,14 +795,12 @@ bool hasAccountSave(Title *title, bool inSD, bool iine, uint32_t user, uint8_t s if (!inSD) { sprintf(srcPath, "storage_slccmpt01:/title/%08x/%08x/data", highID, lowID); } else { - strcpy(srcPath, getBackupPath(highID, lowID, slot).c_str()); + strcpy(srcPath, getDynamicBackupPath(highID, lowID, slot).c_str()); } } - if (checkEntry(srcPath) == 2) { - if (!folderEmpty(srcPath)) { + if (checkEntry(srcPath) == 2) + if (!folderEmpty(srcPath)) return true; - } - } return false; } @@ -804,7 +819,7 @@ bool hasCommonSave(Title *title, bool inSD, bool iine, uint8_t slot, int version srcPath = StringUtils::stringFormat("%s/%08x/%08x/%s/common", path, highID, lowID, "user"); } else { if (!iine) { - srcPath = getBackupPath(highID, lowID, slot) + "/common"; + srcPath = getDynamicBackupPath(highID, lowID, slot) + "/common"; } else { if (!getLoadiineGameSaveDir(srcPath.data(), title->productCode, title->longName, title->highID, title->lowID)) return false; @@ -836,7 +851,7 @@ static void FSAMakeQuotaFromDir(const char *src_path, const char *dst_path, uint closedir(src_dir); } -void copySavedata(Title *title, Title *titleb, int8_t allusers, int8_t allusers_d, bool common) { +void copySavedata(Title *title, Title *titleb, int8_t wiiuuser, int8_t wiiuuser_d, bool common) { uint32_t highID = title->highID; uint32_t lowID = title->lowID; bool isUSB = title->isTitleOnUSB; @@ -848,7 +863,7 @@ void copySavedata(Title *title, Title *titleb, int8_t allusers, int8_t allusers_ return; int slotb = getEmptySlot(titleb->highID, titleb->lowID); if ((slotb >= 0) && promptConfirm(ST_YES_NO, LanguageUtils::gettext("Backup current savedata first to next empty slot?"))) { - backupSavedata(titleb, slotb, allusers, common); + backupSavedata(titleb, slotb, wiiuuser, common); promptError(LanguageUtils::gettext("Backup done. Now copying Savedata.")); } @@ -857,23 +872,32 @@ void copySavedata(Title *title, Title *titleb, int8_t allusers, int8_t allusers_ std::string srcPath = StringUtils::stringFormat("%s/%08x/%08x/%s", path.c_str(), highID, lowID, "user"); std::string dstPath = StringUtils::stringFormat("%s/%08x/%08x/%s", pathb.c_str(), highIDb, lowIDb, "user"); createFolderUnlocked(dstPath); + bool commonSaved = false; - if (allusers > -1) { + if (wiiuuser > -1) { if (common) { FSAMakeQuota(handle, newlibtoFSA(dstPath + "/common").c_str(), 0x666, titleb->accountSaveSize); - if (!copyDir(srcPath + "/common", dstPath + "/common")) + if (copyDir(srcPath + "/common", dstPath + "/common")) + commonSaved = true; + else promptError(LanguageUtils::gettext("Common save not found.")); } - srcPath.append(StringUtils::stringFormat("/%s", wiiuacc[allusers].persistentID)); - dstPath.append(StringUtils::stringFormat("/%s", wiiuacc[allusers_d].persistentID)); - FSAMakeQuota(handle, newlibtoFSA(dstPath).c_str(), 0x666, titleb->accountSaveSize); + srcPath.append(StringUtils::stringFormat("/%s", wiiuacc[wiiuuser].persistentID)); + dstPath.append(StringUtils::stringFormat("/%s", wiiuacc[wiiuuser_d].persistentID)); + if (checkEntry(srcPath.c_str()) == 2) { + FSAMakeQuota(handle, newlibtoFSA(dstPath).c_str(), 0x666, titleb->accountSaveSize); + if (!copyDir(srcPath, dstPath)) + promptError(LanguageUtils::gettext("Copy failed.")); + } + else + if (!commonSaved) + promptError(LanguageUtils::gettext("No save found for this user.")); } else { FSAMakeQuotaFromDir(srcPath.c_str(), dstPath.c_str(), titleb->accountSaveSize); + if (!copyDir(srcPath, dstPath)) + promptError(LanguageUtils::gettext("Copy failed.")); } - if (!copyDir(srcPath, dstPath)) - promptError(LanguageUtils::gettext("Copy failed.")); - if (!titleb->saveInit) { std::string userPath = StringUtils::stringFormat("%s/%08x/%08x/user", pathb.c_str(), highIDb, lowIDb); @@ -913,40 +937,57 @@ void copySavedata(Title *title, Title *titleb, int8_t allusers, int8_t allusers_ } } -void backupAllSave(Title *titles, int count, OSCalendarTime *date) { - OSCalendarTime dateTime; - if (date) { - if (date->tm_year == 0) { - OSTicksToCalendarTime(OSGetTime(), date); - date->tm_mon++; - } - dateTime = (*date); - } else { - OSTicksToCalendarTime(OSGetTime(), &dateTime); - dateTime.tm_mon++; - } +std::string getNowDate() { + OSCalendarTime now; + OSTicksToCalendarTime(OSGetTime(), &now); + return StringUtils::stringFormat("%02d/%02d/%d %02d:%02d", now.tm_mday, ++now.tm_mon, now.tm_year, now.tm_hour, now.tm_min); +} - std::string datetime = StringUtils::stringFormat("%04d-%02d-%02dT%02d%02d%02d", dateTime.tm_year, dateTime.tm_mon, dateTime.tm_mday, - dateTime.tm_hour, dateTime.tm_min, dateTime.tm_sec); - for (int i = 0; i < count; i++) { - if (titles[i].highID == 0 || titles[i].lowID == 0 || !titles[i].saveInit) - continue; +std::string getNowDateForFolder() { + OSCalendarTime now; + OSTicksToCalendarTime(OSGetTime(), &now); + return StringUtils::stringFormat("%04d-%02d-%02dT%02d%02d%02d", now.tm_year, ++now.tm_mon, now.tm_mday, + now.tm_hour, now.tm_min, now.tm_sec); +} + +void writeMetadata(uint32_t highID,uint32_t lowID,uint8_t slot,bool isUSB) { + Metadata *metadataObj = new Metadata(highID, lowID, slot); + metadataObj->set(getNowDate(),isUSB); + delete metadataObj; +} - uint32_t highID = titles[i].highID; - uint32_t lowID = titles[i].lowID; - bool isUSB = titles[i].isTitleOnUSB; - bool isWii = ((highID & 0xFFFFFFF0) == 0x00010000); - const std::string path = (isWii ? "storage_slccmpt01:/title" : (isUSB ? (getUSB() + "/usr/save").c_str() : "storage_mlc01:/usr/save")); - std::string srcPath = StringUtils::stringFormat("%s/%08x/%08x/%s", path.c_str(), highID, lowID, isWii ? "data" : "user"); - std::string dstPath = StringUtils::stringFormat("%s/batch/%s/%08x%08x/0", backupPath, datetime.c_str(), highID, lowID); +void writeMetadata(uint32_t highID,uint32_t lowID,uint8_t slot,bool isUSB, const std::string &batchDatetime) { + Metadata *metadataObj = new Metadata(highID, lowID, slot, batchDatetime); + metadataObj->set(getNowDate(),isUSB); + delete metadataObj; +} - createFolder(dstPath.c_str()); - if (!copyDir(srcPath, dstPath)) - promptError(LanguageUtils::gettext("Backup failed.")); +void backupAllSave(Title *titles, int count, const std::string &batchDatetime) { + for ( int sourceStorage = 0; sourceStorage < 2 ; sourceStorage++ ) { + for (int i = 0; i < count; i++) { + if (titles[i].highID == 0 || titles[i].lowID == 0 || !titles[i].saveInit) + continue; + uint32_t highID = titles[i].highID; + uint32_t lowID = titles[i].lowID; + bool isUSB = titles[i].isTitleOnUSB; + bool isWii = ((highID & 0xFFFFFFF0) == 0x00010000); + if ((sourceStorage == 0 && !isUSB) || (sourceStorage == 1 && isUSB)) // backup first WiiU USB savedata to slot 0 + continue; + uint8_t slot = getEmptySlot(highID,lowID,batchDatetime); + const std::string path = (isWii ? "storage_slccmpt01:/title" : (isUSB ? (getUSB() + "/usr/save").c_str() : "storage_mlc01:/usr/save")); + std::string srcPath = StringUtils::stringFormat("%s/%08x/%08x/%s", path.c_str(), highID, lowID, isWii ? "data" : "user"); + std::string dstPath = getBatchBackupPath(highID,lowID,slot,batchDatetime); + + createFolder(dstPath.c_str()); + if (!copyDir(srcPath, dstPath)) + promptError(LanguageUtils::gettext("Backup failed.")); + else + writeMetadata(highID,lowID,slot,isUSB,batchDatetime); + } } } -void backupSavedata(Title *title, uint8_t slot, int8_t allusers, bool common) { +void backupSavedata(Title *title, uint8_t slot, int8_t wiiuuser, bool common) { if (!isSlotEmpty(title->highID, title->lowID, slot) && !promptConfirm(ST_WARNING, LanguageUtils::gettext("Backup found on this slot. Overwrite it?"))) { return; @@ -958,83 +999,85 @@ void backupSavedata(Title *title, uint8_t slot, int8_t allusers, bool common) { const std::string path = (isWii ? "storage_slccmpt01:/title" : (isUSB ? (getUSB() + "/usr/save").c_str() : "storage_mlc01:/usr/save")); std::string srcPath = StringUtils::stringFormat("%s/%08x/%08x/%s", path.c_str(), highID, lowID, isWii ? "data" : "user"); std::string dstPath; - if (isWii && (slot == 255)) - dstPath = StringUtils::stringFormat("%s/%08x%08x", legacyBackupPath, highID, lowID); - else - dstPath = getBackupPath(highID, lowID, slot); + dstPath = getDynamicBackupPath(highID, lowID, slot); createFolder(dstPath.c_str()); - - if ((allusers > -1) && !isWii) { + bool commonSaved = false; + if ((wiiuuser > -1) && !isWii) { if (common) { - srcPath.append("/common"); - dstPath.append("/common"); - if (!copyDir(srcPath, dstPath)) + if (copyDir(srcPath+"/common", dstPath+"/common")) + commonSaved = true; + else promptError(LanguageUtils::gettext("Common save not found.")); } - srcPath.append(StringUtils::stringFormat("/%s", wiiuacc[allusers].persistentID)); - dstPath.append(StringUtils::stringFormat("/%s", wiiuacc[allusers].persistentID)); + srcPath.append(StringUtils::stringFormat("/%s", wiiuacc[wiiuuser].persistentID)); + dstPath.append(StringUtils::stringFormat("/%s", wiiuacc[wiiuuser].persistentID)); if (checkEntry(srcPath.c_str()) == 0) { - promptError(LanguageUtils::gettext("No save found for this user.")); + if (commonSaved) + writeMetadata(highID,lowID,slot,isUSB); + else + promptError(LanguageUtils::gettext("No save found for this user.")); return; } } if (!copyDir(srcPath, dstPath)) promptError(LanguageUtils::gettext("Backup failed. DO NOT restore from this slot.")); - OSCalendarTime now; - OSTicksToCalendarTime(OSGetTime(), &now); - const std::string date = StringUtils::stringFormat("%02d/%02d/%d %02d:%02d", now.tm_mday, ++now.tm_mon, now.tm_year, now.tm_hour, now.tm_min); - Date *dateObj = new Date(title->highID, title->lowID, slot); - dateObj->set(date); - delete dateObj; - if (dstPath.rfind("storage_slccmpt01:", 0) == 0) { - FSAFlushVolume(handle, "/vol/storage_slccmpt01"); - } else if (dstPath.rfind("storage_mlc01:", 0) == 0) { - FSAFlushVolume(handle, "/vol/storage_mlc01"); - } else if (dstPath.rfind("storage_usb01:", 0) == 0) { - FSAFlushVolume(handle, "/vol/storage_usb01"); - } else if (dstPath.rfind("storage_usb02:", 0) == 0) { - FSAFlushVolume(handle, "/vol/storage_usb02"); - } + else + writeMetadata(highID,lowID,slot,isUSB); + } -void restoreSavedata(Title *title, uint8_t slot, int8_t sdusers, int8_t allusers, bool common) { +void restoreSavedata(Title *title, uint8_t slot, int8_t sduser, int8_t wiiuuser, bool common) { if (isSlotEmpty(title->highID, title->lowID, slot)) { promptError(LanguageUtils::gettext("No backup found on selected slot.")); return; } if (!promptConfirm(ST_WARNING, LanguageUtils::gettext("Are you sure?"))) return; + // backups to ROOT backupSet + BackupSetList::saveBackupSetSubPath(); + BackupSetList::setBackupSetSubPathToRoot(); int slotb = getEmptySlot(title->highID, title->lowID); if ((slotb >= 0) && promptConfirm(ST_YES_NO, LanguageUtils::gettext("Backup current savedata first to next empty slot?"))) - backupSavedata(title, slotb, allusers, common); + backupSavedata(title, slotb, wiiuuser, common); + BackupSetList::restoreBackupSetSubPath(); uint32_t highID = title->highID; uint32_t lowID = title->lowID; bool isUSB = title->isTitleOnUSB; bool isWii = ((highID & 0xFFFFFFF0) == 0x00010000); std::string srcPath; + srcPath = getDynamicBackupPath(highID, lowID, slot); const std::string path = (isWii ? "storage_slccmpt01:/title" : (isUSB ? (getUSB() + "/usr/save").c_str() : "storage_mlc01:/usr/save")); - if (isWii && (slot == 255)) - srcPath = StringUtils::stringFormat("%s/%08x%08x", legacyBackupPath, highID, lowID); - else - srcPath = getBackupPath(highID, lowID, slot); std::string dstPath = StringUtils::stringFormat("%s/%08x/%08x/%s", path.c_str(), highID, lowID, isWii ? "data" : "user"); createFolderUnlocked(dstPath); - if ((sdusers > -1) && !isWii) { + bool commonSaved = false; + if ((sduser > -1) && !isWii) { if (common) { FSAMakeQuota(handle, newlibtoFSA(dstPath + "/common").c_str(), 0x666, title->accountSaveSize); - if (!copyDir(srcPath + "/common", dstPath + "/common")) - promptError(LanguageUtils::gettext("Common save not found.")); + if (copyDir(srcPath + "/common", dstPath + "/common")) + commonSaved = true; + else + promptError(LanguageUtils::gettext("Common save not restored.")); } - srcPath.append(StringUtils::stringFormat("/%s", sdacc[sdusers].persistentID)); - dstPath.append(StringUtils::stringFormat("/%s", wiiuacc[allusers].persistentID)); - FSAMakeQuota(handle, newlibtoFSA(dstPath).c_str(), 0x666, title->accountSaveSize); - } else { + srcPath.append(StringUtils::stringFormat("/%s", sdacc[sduser].persistentID)); + dstPath.append(StringUtils::stringFormat("/%s", wiiuacc[wiiuuser].persistentID)); + if (checkEntry(srcPath.c_str()) == 2) { + FSAMakeQuota(handle, newlibtoFSA(dstPath).c_str(), 0x666, title->accountSaveSize); + if (!copyDir(srcPath, dstPath)) + promptError(LanguageUtils::gettext("Restore failed.")); + } + else + if (!commonSaved) + promptError(LanguageUtils::gettext("No save found for this user.")); + } + else + { FSAMakeQuotaFromDir(srcPath.c_str(), dstPath.c_str(), title->accountSaveSize); + if (!copyDir(srcPath, dstPath)) + promptError(LanguageUtils::gettext("Restore failed.")); } - if (!copyDir(srcPath, dstPath)) - promptError(LanguageUtils::gettext("Restore failed.")); + if (!title->saveInit && !isWii) { @@ -1076,12 +1119,12 @@ void restoreSavedata(Title *title, uint8_t slot, int8_t sdusers, int8_t allusers } } -void wipeSavedata(Title *title, int8_t allusers, bool common) { +void wipeSavedata(Title *title, int8_t wiiuuser, bool common) { if (!promptConfirm(ST_WARNING, LanguageUtils::gettext("Are you sure?")) || !promptConfirm(ST_WARNING, LanguageUtils::gettext("Hm, are you REALLY sure?"))) return; int slotb = getEmptySlot(title->highID, title->lowID); if ((slotb >= 0) && promptConfirm(ST_YES_NO, LanguageUtils::gettext("Backup current savedata first?"))) - backupSavedata(title, slotb, allusers, common); + backupSavedata(title, slotb, wiiuuser, common); uint32_t highID = title->highID; uint32_t lowID = title->lowID; bool isUSB = title->isTitleOnUSB; @@ -1091,25 +1134,26 @@ void wipeSavedata(Title *title, int8_t allusers, bool common) { std::string path; path = (isWii ? "storage_slccmpt01:/title" : (isUSB ? (getUSB() + "/usr/save") : "storage_mlc01:/usr/save")); srcPath = StringUtils::stringFormat("%s/%08x/%08x/%s", path.c_str(), highID, lowID, isWii ? "data" : "user"); - if ((allusers > -1) && !isWii) { + if ((wiiuuser > -1) && !isWii) { if (common) { - srcPath += "/common"; - origPath = srcPath; - if (!removeDir(srcPath)) + origPath = srcPath + "/common"; + if (!removeDir(origPath)) promptError(LanguageUtils::gettext("Common save not found.")); if (unlink(origPath.c_str()) == -1) - promptError(LanguageUtils::gettext("Failed to delete common folder.\n%s"), strerror(errno)); + promptError(LanguageUtils::gettext("Failed to delete common folder: %s"), strerror(errno)); } - srcPath += "/" + std::string(wiiuacc[allusers].persistentID); - origPath = srcPath; + srcPath += "/" + std::string(wiiuacc[wiiuuser].persistentID); } - if (!removeDir(srcPath)) - promptError(LanguageUtils::gettext("Failed to delete savefile.")); - if ((allusers > -1) && !isWii) { - if (unlink(origPath.c_str()) == -1) - promptError(LanguageUtils::gettext("Failed to delete user folder.\n%s"), strerror(errno)); + if (checkEntry(srcPath.c_str()) == 2) { + if (!removeDir(srcPath)) + promptError(LanguageUtils::gettext("Failed to delete savefile.")); + if ((wiiuuser > -1) && !isWii) { + if (unlink(srcPath.c_str()) == -1) + promptError(LanguageUtils::gettext("Failed to delete user folder: %s"), strerror(errno)); + } } + std::string volPath; if (srcPath.find("_usb01") != std::string::npos) { volPath = "/vol/storage_usb01"; diff --git a/src/utils/LanguageUtils.cpp b/src/utils/LanguageUtils.cpp index 51f2b7d..468145a 100644 --- a/src/utils/LanguageUtils.cpp +++ b/src/utils/LanguageUtils.cpp @@ -25,6 +25,7 @@ void LanguageUtils::loadLanguage(Swkbd_LanguageType language) { break; */ case Swkbd_LanguageType__German: + DrawUtils::setFont(OS_SHAREDDATATYPE_FONT_STANDARD); gettextLoadLanguage("romfs:/german.json"); break; case Swkbd_LanguageType__Italian: @@ -49,6 +50,7 @@ void LanguageUtils::loadLanguage(Swkbd_LanguageType language) { break; */ case Swkbd_LanguageType__Portuguese: + DrawUtils::setFont(OS_SHAREDDATATYPE_FONT_STANDARD); gettextLoadLanguage("romfs:/portuguese.json"); break; case Swkbd_LanguageType__Russian: diff --git a/src/utils/StateUtils.cpp b/src/utils/StateUtils.cpp index da6b270..32c2eb4 100644 --- a/src/utils/StateUtils.cpp +++ b/src/utils/StateUtils.cpp @@ -22,7 +22,6 @@ void State::init() { WHBProcInit(); } - uint32_t State::ConsoleProcCallbackAcquired(void *context) {