DEADSOFTWARE

Fix saves
[cavedroid.git] / core / src / ru / deadsoftware / cavedroid / game / GameSaver.java
1 package ru.deadsoftware.cavedroid.game;
3 import com.badlogic.gdx.Gdx;
4 import com.badlogic.gdx.files.FileHandle;
5 import ru.deadsoftware.cavedroid.MainConfig;
6 import ru.deadsoftware.cavedroid.game.mobs.MobsController;
7 import ru.deadsoftware.cavedroid.game.model.block.Block;
8 import ru.deadsoftware.cavedroid.game.objects.DropController;
9 import ru.deadsoftware.cavedroid.game.world.GameWorld;
11 import javax.annotation.CheckForNull;
12 import java.io.*;
13 import java.nio.ByteBuffer;
14 import java.util.HashMap;
15 import java.util.Map;
17 public class GameSaver {
19 private static final String TAG = "GameSaver";
21 public static class Data {
22 @CheckForNull
23 private MobsController mMobsController;
24 @CheckForNull
25 private DropController mDropController;
26 @CheckForNull
27 private Block[][] mForeMap, mBackMap;
29 public Data(MobsController mobsController, DropController dropController, Block[][] foreMap, Block[][] backMap) {
30 mMobsController = mobsController;
31 mDropController = dropController;
32 mForeMap = foreMap;
33 mBackMap = backMap;
34 }
36 public MobsController retrieveMobsController() {
37 assert mMobsController != null;
38 MobsController mobsController = mMobsController;
39 mMobsController = null;
40 return mobsController;
41 }
43 public DropController retrieveDropController() {
44 assert mDropController != null;
45 DropController dropController = mDropController;
46 mDropController = null;
47 return dropController;
48 }
50 public Block[][] retrieveForeMap() {
51 assert mForeMap != null;
52 Block[][] foreMap = mForeMap;
53 mForeMap = null;
54 return foreMap;
55 }
57 public Block[][] retrieveBackMap() {
58 assert mBackMap != null;
59 Block[][] backMap = mBackMap;
60 mBackMap = null;
61 return backMap;
62 }
64 public boolean isEmpty() {
65 return mMobsController == null && mDropController == null && mForeMap == null && mBackMap == null;
66 }
67 }
69 private static final int SAVE_VERSION = 1;
71 private static byte[] intToBytes(int i) {
72 return ByteBuffer.allocate(4).putInt(i).array();
73 }
75 private static Map<String, Integer> buildBlocksDictionary(Block[][] foreMap, Block[][] backMap) {
76 final HashMap<String, Integer> dict = new HashMap<>();
78 int id = 0;
79 for (int i = 0; i < foreMap.length; i++) {
80 for (int j = 0; j < foreMap[i].length; j++) {
81 for (int k = 0; k < 2; k++) {
82 final Block block = k == 0 ? foreMap[i][j] : backMap[i][j];
83 final String key = block.getParams().getKey();
84 if (!dict.containsKey(key)) {
85 dict.put(key, id++);
86 }
87 }
88 }
89 }
91 return dict;
92 }
94 private static void saveDict(FileHandle file, Map<String, Integer> dict) {
95 final String[] arr = new String[dict.size()];
97 for (Map.Entry<String, Integer> entry : dict.entrySet()) {
98 arr[entry.getValue()] = entry.getKey();
99 }
101 final StringBuilder builder = new StringBuilder();
102 for (String key : arr) {
103 builder.append(key);
104 builder.append('\n');
107 file.writeString(builder.toString(), false);
110 private static String[] loadDict(FileHandle file) {
111 return file.readString().split("\n");
114 private static void saveMap(FileHandle file, Block[][] map, Map<String, Integer> dict) throws IOException {
115 int run, block;
116 int width = map.length;
117 int height = map[0].length;
119 BufferedOutputStream out = new BufferedOutputStream(file.write(false));
121 out.write(intToBytes(SAVE_VERSION));
122 out.write(intToBytes(width));
123 out.write(intToBytes(height));
125 for (int y = 0; y < height; y++) {
126 block = dict.get(map[0][y].getParams().getKey());
127 run = 0;
128 for (Block[] blocks : map) {
129 int newValue = dict.get(blocks[y].getParams().getKey());
130 if (newValue != block) {
131 out.write(intToBytes(run));
132 out.write(intToBytes(block));
133 run = 0;
134 block = dict.get(blocks[y].getParams().getKey());
136 run++;
138 out.write(intToBytes(run));
139 out.write(intToBytes(block));
142 out.flush();
143 out.close();
146 private static Block[][] loadMap(GameItemsHolder gameItemsHolder, FileHandle file, String[] dict) throws Exception {
147 Block[][] map;
148 int version, width, height;
149 int run, block;
151 DataInputStream in = new DataInputStream(file.read());
153 version = in.readInt();
155 if (SAVE_VERSION == version) {
156 width = in.readInt();
157 height = in.readInt();
158 map = new Block[width][height];
159 for (int y = 0; y < height; y++) {
160 for (int x = 0; x < width; x += run) {
161 run = in.readInt();
162 block = in.readInt();
163 for (int i = x; i < x + run; i++) {
164 map[i][y] = gameItemsHolder.getBlock(dict[block]);
168 } else {
169 throw new Exception("version mismatch");
172 in.close();
173 return map;
176 @CheckForNull
177 public static Data load(MainConfig mainConfig, GameItemsHolder gameItemsHolder) {
178 String folder = mainConfig.getGameFolder();
179 FileHandle file = Gdx.files.absolute(folder + "/saves/game.sav");
181 try {
182 ObjectInputStream in = new ObjectInputStream(file.read());
183 int version = in.readInt();
184 DropController dropController;
185 MobsController mobsController;
187 if (SAVE_VERSION == version) {
188 dropController = (DropController) in.readObject();
189 mobsController = (MobsController) in.readObject();
190 } else {
191 throw new Exception("version mismatch");
194 in.close();
196 final String[] dict = loadDict(Gdx.files.absolute(mainConfig.getGameFolder() + "/saves/dict"));
197 Block[][] foreMap = loadMap(gameItemsHolder, Gdx.files.absolute(mainConfig.getGameFolder() + "/saves/foremap.sav"), dict);
198 Block[][] backMap = loadMap(gameItemsHolder, Gdx.files.absolute(mainConfig.getGameFolder() + "/saves/backmap.sav"), dict);
200 if (dropController == null || mobsController == null) {
201 throw new Exception("couldn't load");
204 return new Data(mobsController, dropController, foreMap, backMap);
205 } catch (Exception e) {
206 Gdx.app.error("GameSaver", e.getMessage());
209 return null;
212 public static void save(MainConfig mainConfig,
213 DropController dropController,
214 MobsController mobsController,
215 GameWorld gameWorld) {
216 String folder = mainConfig.getGameFolder();
217 FileHandle file = Gdx.files.absolute(folder + "/saves/");
218 file.mkdirs();
219 file = Gdx.files.absolute(folder + "/saves/game.sav");
221 final Block[][] foreMap, backMap;
222 foreMap = gameWorld.getFullForeMap();
223 backMap = gameWorld.getFullBackMap();
225 final Map<String, Integer> dict = buildBlocksDictionary(foreMap, backMap);
227 try {
228 ObjectOutputStream out = new ObjectOutputStream(file.write(false));
229 out.writeInt(SAVE_VERSION);
230 out.writeObject(dropController);
231 out.writeObject(mobsController);
232 out.close();
234 saveDict(Gdx.files.absolute(folder + "/saves/dict"), dict);
235 saveMap(Gdx.files.absolute(folder + "/saves/foremap.sav"), gameWorld.getFullForeMap(), dict);
236 saveMap(Gdx.files.absolute(folder + "/saves/backmap.sav"), gameWorld.getFullBackMap(), dict);
237 } catch (Exception e) {
238 e.printStackTrace();
242 public static boolean exists(MainConfig mainConfig) {
243 String folder = mainConfig.getGameFolder();
244 return (Gdx.files.absolute(folder + "/saves/game.sav").exists() &&
245 Gdx.files.absolute(folder + "/saves/foremap.sav").exists() &&
246 Gdx.files.absolute(folder + "/saves/backmap.sav").exists());