diff --git a/SPD-classes/src/main/java/com/watabou/utils/FileUtils.java b/SPD-classes/src/main/java/com/watabou/utils/FileUtils.java new file mode 100644 index 000000000..1c767186b --- /dev/null +++ b/SPD-classes/src/main/java/com/watabou/utils/FileUtils.java @@ -0,0 +1,138 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2017 Evan Debenham + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ + +package com.watabou.utils; + +import com.watabou.noosa.Game; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class FileUtils { + + // Files + + public static boolean fileExists( String name ){ + File file = new File(Game.instance.getFilesDir(), name); + return file.exists() && !file.isDirectory(); + } + + public static File getFile( String name ){ + return getFile( Game.instance.getFilesDir(), name); + } + + public static File getFile( File base, String name ){ + File file = new File(base, name); + if (!file.exists() || !file.isDirectory()){ + return file; + } + return null; + } + + public static boolean deleteFile( String name ){ + return Game.instance.deleteFile( name ); + } + + public static boolean deleteFile( File file ){ + return !file.isDirectory() && file.delete(); + } + + + // Directories + + public static boolean dirExists( String name ){ + File dir = new File(Game.instance.getFilesDir(), name); + return dir.exists() && dir.isDirectory(); + } + + //base directory + public static File getDir( String name ){ + return getDir( Game.instance.getFilesDir(), name); + } + + public static File getDir( File base, String name ){ + File dir = new File(base, name); + if (!dir.exists() && dir.mkdirs()){ + return dir; + } else if (dir.isDirectory()){ + return dir; + } + return null; + } + + public static boolean deleteDir( String name ){ + return deleteDir(getDir(name)); + } + + public static boolean deleteDir( File dir ){ + if (dir == null || !dir.isDirectory()){ + return false; + } + + for (File f : dir.listFiles()){ + if (f.isDirectory()){ + if (!deleteDir(f)) return false; + } else { + if (!deleteFile(f)) return false; + } + } + + return dir.delete(); + } + + // bundle reading + + //only works for base path + public static Bundle bundleFromFile( String fileName ) throws IOException{ + return bundleFromStream(Game.instance.openFileInput( fileName )); + } + + public static Bundle bundleFromFile( File file ) throws IOException{ + return bundleFromStream(new FileInputStream(file)); + } + + private static Bundle bundleFromStream( InputStream input ) throws IOException{ + Bundle bundle = Bundle.read( input ); + input.close(); + return bundle; + } + + // bundle writing + + //only works for base path + public static void bundleToFile( String fileName, Bundle bundle ) throws IOException{ + bundleToStream( Game.instance.openFileOutput( fileName, Game.MODE_PRIVATE ), bundle); + } + + public static void bundleToFile( File file, Bundle bundle ) throws IOException{ + bundleToStream( new FileOutputStream(file), bundle); + } + + private static void bundleToStream( OutputStream output, Bundle bundle ) throws IOException{ + Bundle.write( bundle, output ); + output.close(); + } + +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Badges.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Badges.java index 2e8046be0..b91d1ec0a 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Badges.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Badges.java @@ -37,13 +37,11 @@ import com.shatteredpixel.shatteredpixeldungeon.journal.Catalog; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene; import com.shatteredpixel.shatteredpixeldungeon.utils.GLog; -import com.watabou.noosa.Game; import com.watabou.utils.Bundle; import com.watabou.utils.Callback; +import com.watabou.utils.FileUtils; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -222,10 +220,7 @@ public class Badges { public static void loadGlobal() { if (global == null) { try { - InputStream input = Game.instance.openFileInput( BADGES_FILE ); - Bundle bundle = Bundle.read( input ); - input.close(); - + Bundle bundle = FileUtils.bundleFromFile( BADGES_FILE ); global = restore( bundle ); } catch (IOException e) { @@ -241,9 +236,7 @@ public class Badges { store( bundle, global ); try { - OutputStream output = Game.instance.openFileOutput( BADGES_FILE, Game.MODE_PRIVATE ); - Bundle.write( bundle, output ); - output.close(); + FileUtils.bundleToFile(BADGES_FILE, bundle); saveNeeded = false; } catch (IOException e) { ShatteredPixelDungeon.reportException(e); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Bones.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Bones.java index 277ed784a..21582fe03 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Bones.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Bones.java @@ -29,11 +29,10 @@ import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.Artifact; import com.shatteredpixel.shatteredpixeldungeon.items.potions.PotionOfHealing; import com.watabou.noosa.Game; import com.watabou.utils.Bundle; +import com.watabou.utils.FileUtils; import com.watabou.utils.Random; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.util.ArrayList; import java.util.Iterator; @@ -64,9 +63,7 @@ public class Bones { bundle.put( ITEM, item ); try { - OutputStream output = Game.instance.openFileOutput( BONES_FILE, Game.MODE_PRIVATE ); - Bundle.write( bundle, output ); - output.close(); + FileUtils.bundleToFile( BONES_FILE, bundle ); } catch (IOException e) { ShatteredPixelDungeon.reportException(e); } @@ -127,9 +124,7 @@ public class Bones { if (depth == -1) { try { - InputStream input = Game.instance.openFileInput( BONES_FILE ) ; - Bundle bundle = Bundle.read( input ); - input.close(); + Bundle bundle = FileUtils.bundleFromFile(BONES_FILE); depth = bundle.getInt( LEVEL ); item = (Item)bundle.get( ITEM ); @@ -143,7 +138,7 @@ public class Bones { } else { //heroes who are challenged cannot find bones if (depth == Dungeon.depth && Dungeon.challenges == 0) { - Game.instance.deleteFile( BONES_FILE ); + FileUtils.deleteFile( BONES_FILE ); depth = 0; //Enforces artifact uniqueness diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java index 587d837a7..06fe509cf 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java @@ -69,13 +69,13 @@ import com.shatteredpixel.shatteredpixeldungeon.windows.WndResurrect; import com.watabou.noosa.Game; import com.watabou.utils.Bundlable; import com.watabou.utils.Bundle; +import com.watabou.utils.FileUtils; import com.watabou.utils.PathFinder; import com.watabou.utils.Random; import com.watabou.utils.SparseArray; +import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.util.ArrayList; import java.util.HashSet; @@ -431,17 +431,9 @@ public class Dungeon { return Random.Int(5 - floorThisSet) < asLeftThisSet; } - private static final String RG_GAME_FILE = "game.dat"; - private static final String RG_DEPTH_FILE = "depth%d.dat"; - - private static final String WR_GAME_FILE = "warrior.dat"; - private static final String WR_DEPTH_FILE = "warrior%d.dat"; - - private static final String MG_GAME_FILE = "mage.dat"; - private static final String MG_DEPTH_FILE = "mage%d.dat"; - - private static final String RN_GAME_FILE = "ranger.dat"; - private static final String RN_DEPTH_FILE = "ranger%d.dat"; + private static final String GAME_FOLDER = "game%d"; + private static final String GAME_FILE = "game.dat"; + private static final String DEPTH_FILE = "depth%d.dat"; private static final String VERSION = "version"; private static final String SEED = "seed"; @@ -456,33 +448,62 @@ public class Dungeon { private static final String QUESTS = "quests"; private static final String BADGES = "badges"; - public static String gameFile( HeroClass cl ) { + //TODO remove class-based loading + + public static File gameFolder( HeroClass cl ) { switch (cl) { - case WARRIOR: - return WR_GAME_FILE; - case MAGE: - return MG_GAME_FILE; - case HUNTRESS: - return RN_GAME_FILE; - default: - return RG_GAME_FILE; + case WARRIOR: + return gameFolder(1); + case MAGE: + return gameFolder(2); + case ROGUE: default: + return gameFolder(3); + case HUNTRESS: + return gameFolder(4); } } - private static String depthFile( HeroClass cl ) { + + + public static File gameFile( HeroClass cl ) { switch (cl) { case WARRIOR: - return WR_DEPTH_FILE; + return gameFile(1); case MAGE: - return MG_DEPTH_FILE; + return gameFile(2); + case ROGUE: default: + return gameFile(3); case HUNTRESS: - return RN_DEPTH_FILE; - default: - return RG_DEPTH_FILE; + return gameFile(4); } } - public static void saveGame( String fileName ) throws IOException { + public static File depthFile( HeroClass cl, int depth ) { + switch (cl) { + case WARRIOR: + return depthFile(1, depth); + case MAGE: + return depthFile(2, depth); + case ROGUE: default: + return depthFile(3, depth); + case HUNTRESS: + return depthFile(4, depth); + } + } + + public static File gameFolder( int save ){ + return FileUtils.getDir(Messages.format(GAME_FOLDER, save)); + } + + public static File gameFile( int save ){ + return FileUtils.getFile(gameFolder( save ), GAME_FILE); + } + + public static File depthFile( int save, int depth ) { + return FileUtils.getFile( gameFolder(save), Messages.format(DEPTH_FILE, depth)); + } + + public static void saveGame( HeroClass cl ) throws IOException { try { Bundle bundle = new Bundle(); @@ -535,9 +556,7 @@ public class Dungeon { Badges.saveLocal( badges ); bundle.put( BADGES, badges ); - OutputStream output = Game.instance.openFileOutput( fileName, Game.MODE_PRIVATE ); - Bundle.write( bundle, output ); - output.close(); + FileUtils.bundleToFile( gameFile(cl), bundle); } catch (IOException e) { GamesInProgress.setUnknown( hero.heroClass ); @@ -549,17 +568,14 @@ public class Dungeon { Bundle bundle = new Bundle(); bundle.put( LEVEL, level ); - OutputStream output = Game.instance.openFileOutput( - Messages.format( depthFile( hero.heroClass ), depth ), Game.MODE_PRIVATE ); - Bundle.write( bundle, output ); - output.close(); + FileUtils.bundleToFile(depthFile( hero.heroClass, depth), bundle); } public static void saveAll() throws IOException { if (hero != null && hero.isAlive()) { Actor.fixTime(); - saveGame( gameFile( hero.heroClass ) ); + saveGame( hero.heroClass ); saveLevel(); GamesInProgress.set( hero.heroClass, depth, hero.lvl, challenges != 0 ); @@ -573,16 +589,12 @@ public class Dungeon { } public static void loadGame( HeroClass cl ) throws IOException { - loadGame( gameFile( cl ), true ); - } - - public static void loadGame( String fileName ) throws IOException { - loadGame( fileName, false ); + loadGame( cl, true ); } - public static void loadGame( String fileName, boolean fullLoad ) throws IOException { + public static void loadGame( HeroClass cl, boolean fullLoad ) throws IOException { - Bundle bundle = gameBundle( fileName ); + Bundle bundle = FileUtils.bundleFromFile( gameFile( cl ) ); version = bundle.getInt( VERSION ); @@ -674,9 +686,7 @@ public class Dungeon { Dungeon.level = null; Actor.clear(); - InputStream input = Game.instance.openFileInput( Messages.format( depthFile( cl ), depth ) ) ; - Bundle bundle = Bundle.read( input ); - input.close(); + Bundle bundle = FileUtils.bundleFromFile( depthFile( cl, depth)) ; Level level = (Level)bundle.get( LEVEL ); @@ -689,27 +699,15 @@ public class Dungeon { public static void deleteGame( HeroClass cl, boolean deleteLevels ) { - Game.instance.deleteFile( gameFile( cl ) ); + FileUtils.deleteFile(gameFile(cl)); if (deleteLevels) { - int depth = 1; - while (Game.instance.deleteFile( Messages.format( depthFile( cl ), depth ) )) { - depth++; - } + FileUtils.deleteDir(gameFolder(cl)); } GamesInProgress.delete( cl ); } - public static Bundle gameBundle( String fileName ) throws IOException { - - InputStream input = Game.instance.openFileInput( fileName ); - Bundle bundle = Bundle.read( input ); - input.close(); - - return bundle; - } - public static void preview( GamesInProgress.Info info, Bundle bundle ) { info.depth = bundle.getInt( DEPTH ); info.version = bundle.getInt( VERSION ); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/GamesInProgress.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/GamesInProgress.java index b90577823..d1ca60f90 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/GamesInProgress.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/GamesInProgress.java @@ -23,6 +23,7 @@ package com.shatteredpixel.shatteredpixeldungeon; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroClass; import com.watabou.utils.Bundle; +import com.watabou.utils.FileUtils; import java.io.IOException; import java.util.HashMap; @@ -31,6 +32,7 @@ public class GamesInProgress { private static HashMap state = new HashMap(); + //TODO this should check if a game directly exists public static Info check( HeroClass cl ) { if (state.containsKey( cl )) { @@ -42,13 +44,14 @@ public class GamesInProgress { Info info; try { - Bundle bundle = Dungeon.gameBundle( Dungeon.gameFile( cl ) ); + Bundle bundle = FileUtils.bundleFromFile(Dungeon.gameFile(cl)); info = new Info(); - Dungeon.preview( info, bundle ); + Dungeon.preview(info, bundle); //saves from before 0.4.3c are not supported - if (info.version < ShatteredPixelDungeon.v0_4_3c){ + if (info.version < ShatteredPixelDungeon.v0_4_3c) { info = null; + } } catch (IOException e) { diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Rankings.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Rankings.java index c2c30e483..a797a82f0 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Rankings.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Rankings.java @@ -33,13 +33,11 @@ import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.Scroll; import com.shatteredpixel.shatteredpixeldungeon.journal.Notes; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.ui.QuickSlotButton; -import com.watabou.noosa.Game; import com.watabou.utils.Bundlable; import com.watabou.utils.Bundle; +import com.watabou.utils.FileUtils; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -194,9 +192,7 @@ public enum Rankings { bundle.put( WON, wonNumber ); try { - OutputStream output = Game.instance.openFileOutput( RANKINGS_FILE, Game.MODE_PRIVATE ); - Bundle.write( bundle, output ); - output.close(); + FileUtils.bundleToFile( RANKINGS_FILE, bundle); } catch (IOException e) { ShatteredPixelDungeon.reportException(e); } @@ -212,9 +208,7 @@ public enum Rankings { records = new ArrayList<>(); try { - InputStream input = Game.instance.openFileInput( RANKINGS_FILE ); - Bundle bundle = Bundle.read( input ); - input.close(); + Bundle bundle = FileUtils.bundleFromFile( RANKINGS_FILE ); for (Bundlable record : bundle.getCollection( RECORDS )) { records.add( (Record)record ); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ShatteredPixelDungeon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ShatteredPixelDungeon.java index 60ff3bfe3..3f4e3337a 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ShatteredPixelDungeon.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ShatteredPixelDungeon.java @@ -56,6 +56,8 @@ public class ShatteredPixelDungeon extends Game { public static final int v0_6_1 = 205; public static final int v0_6_2 = 222; + public static final int v0_6_2e = 229; + public ShatteredPixelDungeon() { super( WelcomeScene.class ); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/journal/Journal.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/journal/Journal.java index 77cd05005..0398d0b76 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/journal/Journal.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/journal/Journal.java @@ -22,12 +22,10 @@ package com.shatteredpixel.shatteredpixeldungeon.journal; import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon; -import com.watabou.noosa.Game; import com.watabou.utils.Bundle; +import com.watabou.utils.FileUtils; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; public class Journal { @@ -42,9 +40,7 @@ public class Journal { Bundle bundle; try { - InputStream input = Game.instance.openFileInput( JOURNAL_FILE ); - bundle = Bundle.read(input); - input.close(); + bundle = FileUtils.bundleFromFile( JOURNAL_FILE ); } catch (IOException e){ bundle = new Bundle(); @@ -70,9 +66,7 @@ public class Journal { Document.store(bundle); try { - OutputStream output = Game.instance.openFileOutput( JOURNAL_FILE, Game.MODE_PRIVATE ); - Bundle.write( bundle, output ); - output.close(); + FileUtils.bundleToFile( JOURNAL_FILE, bundle ); saveNeeded = false; } catch (IOException e) { ShatteredPixelDungeon.reportException(e); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/WelcomeScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/WelcomeScene.java index 02fb064a5..d41a52017 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/WelcomeScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/WelcomeScene.java @@ -23,6 +23,7 @@ package com.shatteredpixel.shatteredpixeldungeon.scenes; import com.shatteredpixel.shatteredpixeldungeon.Assets; import com.shatteredpixel.shatteredpixeldungeon.Badges; +import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.Rankings; import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon; import com.shatteredpixel.shatteredpixeldungeon.effects.BannerSprites; @@ -35,6 +36,8 @@ import com.watabou.noosa.Camera; import com.watabou.noosa.Game; import com.watabou.noosa.Image; import com.watabou.noosa.audio.Sample; +import com.watabou.utils.Bundle; +import com.watabou.utils.FileUtils; public class WelcomeScene extends PixelScene { @@ -155,7 +158,35 @@ public class WelcomeScene extends PixelScene { Rankings.INSTANCE.save(); } catch (Exception e) { //if we encounter a fatal error, then just clear the rankings - Game.instance.deleteFile( Rankings.RANKINGS_FILE ); + FileUtils.deleteFile( Rankings.RANKINGS_FILE ); + } + } + + //convert game saves from the old format + if (previousVersion <= ShatteredPixelDungeon.v0_6_2e){ + //old save file names for warrior, mage, rogue, huntress + String[] classes = new String[]{"warrior", "mage", "game", "ranger"}; + for (int i = 1; i <= classes.length; i++){ + String name = classes[i-1]; + if (FileUtils.fileExists(name + ".dat")){ + try { + Bundle gamedata = FileUtils.bundleFromFile(name + ".dat"); + FileUtils.bundleToFile(Dungeon.gameFile(i), gamedata); + FileUtils.deleteFile(name + ".dat"); + + //rogue's safe files have a different name + if (name.equals("game")) name = "depth"; + + int depth = 1; + while (FileUtils.fileExists(name + depth + ".dat")) { + gamedata = FileUtils.bundleFromFile(name + depth + ".dat"); + FileUtils.bundleToFile(Dungeon.depthFile(i, depth), gamedata); + FileUtils.deleteFile(name + depth + ".dat"); + depth++; + } + } catch (Exception e){ + } + } } }