add backup/restore classic mode data

This commit is contained in:
Anthony Calosa
2025-08-11 21:53:19 +08:00
parent 3ab50a5fb5
commit 467fff3651
10 changed files with 148 additions and 36 deletions

View File

@@ -1,15 +1,19 @@
package forge.screens.settings;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import com.badlogic.gdx.files.FileHandle;
import com.google.common.collect.ImmutableList;
import forge.StaticData;
import forge.gui.FThreads;
import forge.gui.GuiBase;
import forge.screens.LoadingOverlay;
import forge.util.ZipUtil;
import org.apache.commons.lang3.StringUtils;
import com.badlogic.gdx.utils.Align;
@@ -44,11 +48,50 @@ public class FilesPage extends TabPage<SettingsScreen> {
lstItems.setListItemRenderer(new FilesItemRenderer());
lstItems.addGroup(Forge.getLocalizer().getMessage("lblDataManagement"));
lstItems.addGroup(Forge.getLocalizer().getMessage("lblCardAudit"));
lstItems.addGroup(Forge.getLocalizer().getMessage("ContentDownloaders"));
lstItems.addGroup(Forge.getLocalizer().getMessage("lblStorageLocations"));
//lstItems.addGroup("Data Import");
//Backup and Restore
lstItems.addItem(new Extra(Forge.getLocalizer().getMessage("lblBackupRestore"), Forge.getLocalizer().getMessage("lblBackupRestoreDescription")) {
@Override
public void select() {
FOptionPane.showOptionDialog(Forge.getLocalizer().getMessage("lblPlsSelectActions"), "", FOptionPane.QUESTION_ICON, ImmutableList.of(Forge.getLocalizer().getMessage("lblBackup"), Forge.getLocalizer().getMessage("lblRestore"), Forge.getLocalizer().getMessage("lblCancel")), 2, new Callback<Integer>() {
@Override
public void run(Integer result) {
switch (result) {
case 0:
FThreads.invokeInEdtLater(() -> LoadingOverlay.show(Forge.getLocalizer().getMessage("lblBackupMsg"), true, () -> {
File source = new FileHandle(ForgeProfileProperties.getUserDir()).file();
File target = new FileHandle(Forge.getDeviceAdapter().getDownloadsDir()).file();
try {
ZipUtil.zip(source, target, ZipUtil.backupClsFile);
FOptionPane.showMessageDialog(Forge.getLocalizer().getMessage("lblSuccess") + "\n" + target.getAbsolutePath() + File.separator + ZipUtil.backupClsFile, Forge.getLocalizer().getMessage("lblBackup"), FOptionPane.INFORMATION_ICON);
} catch (IOException e) {
FOptionPane.showMessageDialog(e.toString(), Forge.getLocalizer().getMessage("lblError"), FOptionPane.ERROR_ICON);
}
}));
break;
case 1:
FThreads.invokeInEdtLater(() -> LoadingOverlay.show(Forge.getLocalizer().getMessage("lblRestoreMsg"), true, () -> {
File source = new FileHandle(Forge.getDeviceAdapter().getDownloadsDir() + ZipUtil.backupClsFile).file();
File target = new FileHandle(ForgeProfileProperties.getUserDir()).file().getParentFile();
try {
String msg = ZipUtil.unzip(source, target);
FOptionPane.showMessageDialog(Forge.getLocalizer().getMessage("lblSuccess") + "\n" + msg, Forge.getLocalizer().getMessage("lblRestore"), FOptionPane.INFORMATION_ICON);
} catch (IOException e) {
FOptionPane.showMessageDialog(e.toString(), Forge.getLocalizer().getMessage("lblError"), FOptionPane.ERROR_ICON);
}
}));
break;
default:
break;
}
}
});
}
}, 0);
//Auditer
lstItems.addItem(new Extra(Forge.getLocalizer().getMessage("btnListImageData"), Forge.getLocalizer().getMessage("lblListImageData")) {
@Override
@@ -74,7 +117,7 @@ public class FilesPage extends TabPage<SettingsScreen> {
});
}));
}
}, 0);
}, 1);
//content downloaders
// lstItems.addItem(new ContentDownloader(Forge.getLocalizer().getMessage("btnDownloadPics"),
// Forge.getLocalizer().getMessage("lblDownloadPics")) {
@@ -82,35 +125,35 @@ public class FilesPage extends TabPage<SettingsScreen> {
// protected GuiDownloadService createService() {
// return new GuiDownloadPicturesLQ();
// }
// }, 1);
// }, 2);
// lstItems.addItem(new ContentDownloader(Forge.getLocalizer().getMessage("btnDownloadSetPics"),
// Forge.getLocalizer().getMessage("lblDownloadSetPics")) {
// @Override
// protected GuiDownloadService createService() {
// return new GuiDownloadSetPicturesLQ();
// }
// }, 1);
// }, 2);
// lstItems.addItem(new ContentDownloader(Forge.getLocalizer().getMessage("btnDownloadQuestImages"),
// Forge.getLocalizer().getMessage("lblDownloadQuestImages")) {
// @Override
// protected GuiDownloadService createService() {
// return new GuiDownloadQuestImages();
// }
// }, 1);
// }, 2);
// lstItems.addItem(new ContentDownloader(Forge.getLocalizer().getMessage("btnDownloadAchievementImages"),
// Forge.getLocalizer().getMessage("lblDownloadAchievementImages")) {
// @Override
// protected GuiDownloadService createService() {
// return new GuiDownloadAchievementImages();
// }
// }, 1);
// }, 2);
lstItems.addItem(new ContentDownloader(Forge.getLocalizer().getMessage("btnDownloadPrices"),
Forge.getLocalizer().getMessage("lblDownloadPrices")) {
@Override
protected GuiDownloadService createService() {
return new GuiDownloadPrices();
}
}, 1);
}, 2);
lstItems.addItem(new ContentDownloader(Forge.getLocalizer().getMessage("btnDownloadSkins"),
Forge.getLocalizer().getMessage("lblDownloadSkins")) {
@Override
@@ -121,7 +164,7 @@ public class FilesPage extends TabPage<SettingsScreen> {
protected void finishCallback() {
SettingsScreen.getSettingsScreen().getSettingsPage().refreshSkinsList();
}
}, 1);
}, 2);
lstItems.addItem(new OptionContentDownloader(Forge.getLocalizer().getMessage("btnDownloadCJKFonts"),
Forge.getLocalizer().getMessage("lblDownloadCJKFonts"),
Forge.getLocalizer().getMessage("lblDownloadCJKFontPrompt")) {
@@ -146,7 +189,7 @@ public class FilesPage extends TabPage<SettingsScreen> {
protected void finishCallback() {
SettingsScreen.getSettingsScreen().getSettingsPage().refreshCJKFontsList();
}
}, 1);
}, 2);
//storage locations
final StorageOption cardPicsOption = new StorageOption(Forge.getLocalizer().getMessage("lblCardPicsLocation"), ForgeProfileProperties.getCardPicsDir()) {
@Override
@@ -169,7 +212,7 @@ public class FilesPage extends TabPage<SettingsScreen> {
//ensure decks option is updated if needed
decksOption.updateDir(ForgeProfileProperties.getDecksDir());
}
}, 2);
}, 3);
lstItems.addItem(new StorageOption(Forge.getLocalizer().getMessage("lblImageCacheLocation"), ForgeProfileProperties.getCacheDir()) {
@Override
protected void onDirectoryChanged(String newDir) {
@@ -178,7 +221,7 @@ public class FilesPage extends TabPage<SettingsScreen> {
//ensure card pics option is updated if needed
cardPicsOption.updateDir(ForgeProfileProperties.getCardPicsDir());
}
}, 2);
}, 3);
lstItems.addItem(cardPicsOption, 2);
lstItems.addItem(decksOption, 2);
}

View File

@@ -3529,4 +3529,11 @@ lblDefeatedDescription=Sie sind besiegt und können nicht weitermachen. Mit letz
lblReversePromptButton=Umgekehrte Eingabeaufforderungsschaltflächen
nlReversePromptButton=Wenn diese Option aktiviert ist, sind die Schaltflächen „OK“ und „Cancel“ vertauscht.
lblPrepareDatabase=Datenbank vorbereiten...
lblLoadingGameResources=Spielressourcen werden geladen...
lblLoadingGameResources=Spielressourcen werden geladen...
lblBackupRestore=Sichern und Wiederherstellen
lblBackupRestoreDescription=Sichern oder Wiederherstellen von Daten des klassischen Spielmodus im/aus dem Download-Ordner
lblDataManagement=Datenmanagement
lblPlsSelectActions=Bitte wählen Sie Optionen zum Ausführen der Aktion aus
lblBackupMsg=Sichern von Dateien
lblRestoreMsg=Wiederherstellen von Dateien
lblSuccess=Erfolg

View File

@@ -3276,4 +3276,11 @@ lblDefeatedDescription=Defeated and unable to continue, you use the last of your
lblReversePromptButton=Reversed Prompt Buttons
nlReversePromptButton=If enabled, the OK and Cancel Prompt buttons are reversed.
lblPrepareDatabase=Preparing database...
lblLoadingGameResources=Loading game resources...
lblLoadingGameResources=Loading game resources...
lblBackupRestore=Backup and Restore
lblBackupRestoreDescription=Backup or Restore Classic game mode data to/from Downloads folder
lblDataManagement=Data Management
lblPlsSelectActions=Please select options to perform action
lblBackupMsg=Backing up files
lblRestoreMsg=Restoring files
lblSuccess=Success

View File

@@ -3533,4 +3533,11 @@ lblDefeatedDescription=Derrotado e incapaz de continuar, utilizas lo último de
lblReversePromptButton=Botones de aviso invertidos
nlReversePromptButton=Si esta opción está habilitada, los botones Aceptar y Cancelar solicitud se invierten.
lblPrepareDatabase=Preparando la base de datos...
lblLoadingGameResources=Cargando recursos del juego...
lblLoadingGameResources=Cargando recursos del juego...
lblBackupRestore=Copia de seguridad y restauración
lblBackupRestoreDescription=Realizar una copia de seguridad o restaurar los datos del modo de juego clásico en la carpeta de Descargas
lblDataManagement=Gestión de datos
lblPlsSelectActions=Seleccione las opciones para realizar la acción
lblBackupMsg=Realizar copias de seguridad de archivos
lblRestoreMsg=Restaurando archivos
lblSuccess=Éxito

View File

@@ -3534,4 +3534,11 @@ lblDefeatedDescription=Vaincu et incapable de continuer, vous utilisez le reste
lblReversePromptButton=Boutons d'invite inversés
nlReversePromptButton=Si cette option est activée, les boutons OK et Annuler sont inversés.
lblPrepareDatabase=Préparation de la base de données...
lblLoadingGameResources=Chargement des ressources du jeu...
lblLoadingGameResources=Chargement des ressources du jeu...
lblBackupRestore=Sauvegarde et restauration
lblBackupRestoreDescription=Sauvegarder ou restaurer les données du mode de jeu classique vers/depuis le dossier Téléchargements
lblDataManagement=Gestion des données
lblPlsSelectActions=Veuillez sélectionner les options pour effectuer l'action
lblBackupMsg=Sauvegarde des fichiers
lblRestoreMsg=Restauration de fichiers
lblSuccess=Succès

View File

@@ -3532,4 +3532,11 @@ lblDefeatedDescription=Sconfitto e impossibilitato a proseguire, usi le ultime e
lblReversePromptButton=Pulsanti di richiesta invertiti
nlReversePromptButton=Se abilitati, i pulsanti OK e Annulla richiesta sono invertiti.
lblPrepareDatabase=Preparazione del database...
lblLoadingGameResources=Caricamento delle risorse del gioco...
lblLoadingGameResources=Caricamento delle risorse del gioco...
lblBackupRestore=Backup e ripristino
lblBackupRestoreDescription=Esegui il backup o il ripristino dei dati della modalità di gioco classica nella/dalla cartella Download
lblDataManagement=Gestione dei dati
lblPlsSelectActions=Seleziona le opzioni per eseguire l'azione
lblBackupMsg=Backup dei file
lblRestoreMsg=Ripristino dei file
lblSuccess=Successo

View File

@@ -640,27 +640,27 @@ lblPreconstructedDecks=構築済みデッキ
lblPreconCommanderDecks=構築済み統率者デッキ
lblQuestOpponentDecks=クエスト対戦相手のデッキ
lblRandomColorDecks=ランダムカラーデッキ
lblRandomStandardArchetypeDecks=ランダムアーキタイプデッキ スタンダード
lblRandomPioneerArchetypeDecks=ランダムアーキタイプデッキ パイオニア
lblRandomHistoricArchetypeDecks=ランダムアーキタイプデッキ ヒストリック
lblRandomModernArchetypeDecks=ランダムアーキタイプデッキ モダン
lblRandomLegacyArchetypeDecks=ランダムアーキタイプデッキ レガシー
lblRandomVintageArchetypeDecks=ランダムアーキタイプデッキ ビンテージ
lblRandomStandardArchetypeDecks=ランダムアーキタイプデッキ\u3000スタンダード
lblRandomPioneerArchetypeDecks=ランダムアーキタイプデッキ\u3000パイオニア
lblRandomHistoricArchetypeDecks=ランダムアーキタイプデッキ\u3000ヒストリック
lblRandomModernArchetypeDecks=ランダムアーキタイプデッキ\u3000モダン
lblRandomLegacyArchetypeDecks=ランダムアーキタイプデッキ\u3000レガシー
lblRandomVintageArchetypeDecks=ランダムアーキタイプデッキ\u3000ビンテージ
lblRandomPauperArchetypeDecks=ランダムなPauperアーキタイプデッキ
lblRandomStandardColorDecks=ランダムカラーデッキ スタンダード
lblRandomModernColorDecks=ランダムカラーデッキ モダン
lblRandomStandardColorDecks=ランダムカラーデッキ\u3000スタンダード
lblRandomModernColorDecks=ランダムカラーデッキ\u3000モダン
lblRandomPauperColorDecks=ランダムカラーデッキパウパー
lblRandomThemeDecks=ランダムテーマデッキ
lblRandomDecks=ランダムデッキ
lblNetDecks=ネットデッキ
lblNetCommanderDecks=ネット統率者デッキ
lblNetArchiveStandardDecks=ネットアーカイブデッキ スタンダード
lblNetArchivePioneerDecks=ネットアーカイブデッキ パイオニア
lblNetArchiveModernDecks=ネットアーカイブデッキ モダン
lblNetArchiveLegacyDecks=ネットアーカイブデッキ レガシー
lblNetArchiveVintageDecks=ネットアーカイブデッキ ビンテージ
lblNetArchiveBlockDecks=ネットアーカイブデッキ ブロック
lblNetArchivePauperDecks=ネットアーカイブデッキ パウパー
lblNetArchiveStandardDecks=ネットアーカイブデッキ\u3000スタンダード
lblNetArchivePioneerDecks=ネットアーカイブデッキ\u3000パイオニア
lblNetArchiveModernDecks=ネットアーカイブデッキ\u3000モダン
lblNetArchiveLegacyDecks=ネットアーカイブデッキ\u3000レガシー
lblNetArchiveVintageDecks=ネットアーカイブデッキ\u3000ビンテージ
lblNetArchiveBlockDecks=ネットアーカイブデッキ\u3000ブロック
lblNetArchivePauperDecks=ネットアーカイブデッキ\u3000パウパー
#VSubmenuTutorial
lblTutorial=チュートリアル
lblTutorialMode=チュートリアルモード
@@ -1860,7 +1860,7 @@ lblDoYouWantFlipNCoinAction={0}枚のコイントスをしますか?
lblDoYouWantRollNDiceAction={0}{1}を投げますか?
lblDoYouWantRemoveNTargetTypeCounterFromCard={2}から {1}個の {0}カウンターを取り除きますか?
lblDoYouWantRemoveCountersFromCard={0}からカウンターを取り除きますか?
lblDoYouWantExileNCardsFromYourLibrary=ライブラリーから {0}枚のカードを追放しますか?
lblDoYouWantExileNCardsFromYourLibrary=ライブラリーから\u3000{0}枚のカードを追放しますか?
lblDoYouWantExileAllCardYouGraveyard=墓地から全てのカードを追放しますか?
lblDoYouWantExileAllCardHand=Do you want to exile all cards in your hand?
lblDoYouWantDiscardYourHand=手札を捨てますか?
@@ -3528,4 +3528,11 @@ lblDefeatedDescription=敗北し、続行不可能となったあなたは、最
lblReversePromptButton=逆プロンプトボタン
nlReversePromptButton=有効にすると、[OK] ボタンと [Cancel] ボタンが逆になります。
lblPrepareDatabase=データベースを準備しています
lblLoadingGameResources=ゲームリソースを読み込んでいます
lblLoadingGameResources=ゲームリソースを読み込んでいます
lblBackupRestore=バックアップと復元
lblBackupRestoreDescription=クラシックゲームモードのデータをダウンロードフォルダにバックアップまたは復元する
lblDataManagement=データ管理
lblPlsSelectActions=アクションを実行するにはオプションを選択してください
lblBackupMsg=ファイルのバックアップ
lblRestoreMsg=ファイルの復元
lblSuccess=成功

View File

@@ -3618,4 +3618,11 @@ lblDefeatedDescription=Derrotado e incapaz de continuar, você usa o que resta d
lblReversePromptButton=Botões de prompt invertidos
nlReversePromptButton=Se ativados, os botões OK e Cancelar prompt serão invertidos.
lblPrepareDatabase=Preparando banco de dados...
lblLoadingGameResources=Carregando recursos do jogo...
lblLoadingGameResources=Carregando recursos do jogo...
lblBackupRestore=Backup e restauração
lblBackupRestoreDescription=Fazer backup ou restaurar dados do modo de jogo clássico de/para a pasta Downloads
lblDataManagement=Gestão de Dados
lblPlsSelectActions=Selecione as opções para executar a ação
lblBackupMsg=Fazendo backup de arquivos
lblRestoreMsg=Restaurando arquivos
lblSuccess=Sucesso

View File

@@ -3519,4 +3519,11 @@ lblDefeatedDescription=你被击败了,无法继续前进,你用尽最后的
lblReversePromptButton=反转提示按钮
nlReversePromptButton=如果启用,则“确定”和“取消提示”按钮将会反转。
lblPrepareDatabase=准备数据库
lblLoadingGameResources=正在加载游戏资源
lblLoadingGameResources=正在加载游戏资源
lblBackupRestore=备份和还原
lblBackupRestoreDescription=将经典游戏模式数据备份或恢复到“下载”文件夹
lblDataManagement=数据管理
lblPlsSelectActions=请选择要执行操作的选项
lblBackupMsg=备份文件
lblRestoreMsg=恢复文件
lblSuccess=成功

View File

@@ -14,7 +14,10 @@ import java.util.zip.ZipOutputStream;
*/
public class ZipUtil {
public static String backupAdvFile = "forge.adv";
public static String backupClsFile = "forge.cls";
private static boolean isClassic = false;
public static void zip(File source, File dest, String name) throws IOException {
isClassic = backupClsFile.equalsIgnoreCase(name);
try(
FileOutputStream fos = new FileOutputStream(dest.getAbsolutePath() + File.separator + name);
ZipOutputStream zipOut = new ZipOutputStream(fos)) {
@@ -26,7 +29,13 @@ public class ZipUtil {
if (fileToZip.isHidden()) {
return;
}
//skip loose files like forge.log, etc
if (fileToZip.isFile() && isClassic && "Forge".equalsIgnoreCase(fileToZip.getParentFile().getName()))
return;
if (fileToZip.isDirectory()) {
//skip adventure since we don't want to overwrite it and adventure has its own backup method
if (isClassic && "adventure".equalsIgnoreCase(fileToZip.getName()) && "Forge".equalsIgnoreCase(fileToZip.getParentFile().getName()))
return;
if (fileName.endsWith("/")) {
zipOut.putNextEntry(new ZipEntry(fileName));
zipOut.closeEntry();
@@ -54,6 +63,7 @@ public class ZipUtil {
}
public static String unzip(File fileZip, File destDir) throws IOException {
isClassic = backupClsFile.equalsIgnoreCase(fileZip.getName());
StringBuilder val = new StringBuilder();
byte[] buffer = new byte[1024];
ZipInputStream zis = new ZipInputStream(Files.newInputStream(fileZip.toPath()));
@@ -64,6 +74,8 @@ public class ZipUtil {
if (!newFile.isDirectory() && !newFile.mkdirs()) {
throw new IOException("Failed to create directory " + newFile);
}
if (isClassic && "Forge".equalsIgnoreCase(newFile.getParentFile().getName()))
val.append(" * "). append(newFile.getName()).append("\n");
} else {
// fix for Windows-created archives
File parent = newFile.getParentFile();
@@ -71,8 +83,9 @@ public class ZipUtil {
throw new IOException("Failed to create directory " + parent);
}
if (!isClassic)
val.append(" * "). append(newFile.getName()).append("\n");
// write file content
val.append(" * "). append(newFile.getName()).append("\n");
try(FileOutputStream fos = new FileOutputStream(newFile)) {
int len;
while ((len = zis.read(buffer)) > 0) {