package games.strategy.engine.framework.startup.ui; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import games.strategy.debug.ClientLogger; import games.strategy.engine.ClientFileSystemHelper; import games.strategy.engine.data.GameData; import games.strategy.engine.data.properties.IEditableProperty; /** * A game options cache that uses files to store the game options. */ public class FileBackedGamePropertiesCache implements IGamePropertiesCache { // chars illegal on windows (on linux/mac anything that is allowed on windows works fine) static final char[] s_illegalChars = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 34, 42, 58, 60, 62, 63, 92, 124}; /** * Caches the gameOptions stored in the game data, and associates with this game. only values that are serializable * (which they should all be) will be stored * * @param gameData * the game which options you want to cache */ @Override public void cacheGameProperties(final GameData gameData) { final Map<String, Object> serializableMap = new HashMap<>(); for (final IEditableProperty property : gameData.getProperties().getEditableProperties()) { if (property.getValue() instanceof Serializable) { serializableMap.put(property.getName(), property.getValue()); } } final File cache = getCacheFile(gameData); try { // create the directory if it doesn't already exists if (!cache.getParentFile().exists()) { cache.getParentFile().mkdirs(); } final ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(cache)); out.writeObject(serializableMap); out.close(); } catch (final IOException e) { ClientLogger.logQuietly(e); } } /** * Loads cached game options into the gameData. * * @param gameData * the game to load the cached game options into */ @Override @SuppressWarnings("unchecked") // generics are compile time only, and lost during serialization public void loadCachedGamePropertiesInto(final GameData gameData) { final File cache = getCacheFile(gameData); try { if (cache.exists()) { final ObjectInputStream in = new ObjectInputStream(new FileInputStream(cache)); final Map<String, Serializable> serializedMap = (Map<String, Serializable>) in.readObject(); for (final IEditableProperty property : gameData.getProperties().getEditableProperties()) { final Serializable ser = serializedMap.get(property.getName()); if (ser != null) { property.setValue(ser); } } in.close(); } } catch (IOException | ClassNotFoundException e) { ClientLogger.logQuietly(e); } } /** * Calculates the cache filename and location based on the game data. * * @param gameData * the game data * @return the File where the cached game options should be stored or read from */ private static File getCacheFile(final GameData gameData) { final File cacheDir = new File(ClientFileSystemHelper.getUserRootFolder(), "optionCache"); return new File(cacheDir, getFileName(gameData.getGameName())); } /** * Removes any special characters from the file name. * * @param gameName * the name of the game * @return the fileName on disk */ private static String getFileName(final String gameName) { final StringBuilder sb = new StringBuilder(); for (int i = 0, charArrayLength = gameName.length(); i < charArrayLength; i++) { final char c = gameName.charAt(i); if (Arrays.binarySearch(s_illegalChars, c) < 0) { sb.append(c); } } sb.append(".defaultOptions"); return sb.toString(); } }