package net.sf.colossus.server; import java.io.File; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.logging.Level; import java.util.logging.Logger; import net.sf.colossus.common.Constants; import net.sf.colossus.variant.Variant; import org.jdom.Attribute; import org.jdom.Document; import org.jdom.Element; import org.jdom.input.SAXBuilder; public class GameLoading { private static final Logger LOGGER = Logger.getLogger(GameLoading.class .getName()); private Variant variant; private Element root; public GameLoading() { super(); LOGGER.info("Instantiated GameLoading"); } public Variant getVariant() { return variant; } public Element getRoot() { return root; } /** * Try to load a game from saveDirName/filename. * * If the filename is "--latest" then load the latest savegame that * can be found in saveDirName. * * @return String telling reason for failure, or null if all ok */ public String loadGame(String filename) { File file = resolveFileNameToFile(filename); if (file == null) { return "Can't resolve filename to file: " + filename; } return loadGameFromFile(file); } /** * For a given filename, open the file with that name from * save game directory. * If no such file, try also whether with adding ".xml" it would * become a valid/existing file. * * If the filename is "--latest" then load the latest savegame that * can be found in saveDirName. * * * @param filename The name of the file to load, or "--latest" for * looking for latest save game (snapshot) file. * @return The File object for that filename, or null if no such file * can't be found */ private File resolveFileNameToFile(String filename) { File file = null; if (filename.equals("--latest")) { File dir = new File(Constants.SAVE_DIR_NAME); if (!dir.exists() || !dir.isDirectory()) { LOGGER.log(Level.SEVERE, "No saves directory"); return null; } String[] filenames = dir.list(new XMLSnapshotFilter()); if (filenames.length < 1) { LOGGER.log(Level.SEVERE, "No XML savegames found in saves directory"); return null; } String name = latestSaveFilename(filenames); System.out.println("name " + name); file = new File(Constants.SAVE_DIR_NAME + name); } else if (filename.indexOf("/") >= 0 || filename.indexOf("\\") >= 0) { // Already a full path file = new File(filename); } else { file = new File(Constants.SAVE_DIR_NAME + filename); } if (!file.exists()) { String tryXMLFile = file.getPath() + ".xml"; File xmlFile = new File(tryXMLFile); if (xmlFile.exists()) { LOGGER.warning("Given filename does not exist - loading " + "instead the one with .xml appended to the name!"); file = xmlFile; } else { LOGGER.severe("Cannot load saved game: file " + file.getPath() + " does not exist!"); return null; } } return file; } /** * Load contents of the file, get variant name, load the right variant * and get the root element. * Currently also gets all variant data files and puts them to file * cache but that is going to be removed one day. Soon ;-) * * @param file The file from which to load the game * @return True if load was successful, otherwise false */ public String loadGameFromFile(File file) { try { LOGGER.info("Loading game from " + file); SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(file); this.root = doc.getRootElement(); Attribute ver = root.getAttribute("version"); if (!ver.getValue().equals(Constants.XML_SNAPSHOT_VERSION)) { LOGGER.severe("Can't load this savegame version."); // TODO not only would this fail to load quietly, it also fails // to fail quietly rather noisily by causing an NPE in dispose(). return "Can't load this savegame version (" + ver.getValue() + ", expected: " + Constants.XML_SNAPSHOT_VERSION + ")"; } Element el = root.getChild("Variant"); Attribute namAttr = el.getAttribute("name"); String varName = null; if (namAttr != null) { varName = namAttr.getValue(); } else { LOGGER.severe("Variant name not set in saveGame file!"); return "Variant name not set in saveGame file!"; } this.variant = VariantSupport.loadVariantByName(varName, true); } catch (Exception e) { LOGGER.log(Level.SEVERE, "Exception during loading Game: ", e); return "Exception during loading game: " + e.getMessage(); } return null; } /** * Find from the list of savegame filenames the one with the highest * numerical value (1000000000_xxx.xml comes after 999999999_xxx.xml) * @param filenames An array of strings which represent filenames * @return Latest savegame from the list */ private String latestSaveFilename(String[] filenames) { return Collections.max(Arrays.asList(filenames), new Comparator<String>() { public int compare(String s1, String s2) { long diff = (numberValue(s1) - numberValue(s2)); if (diff > Integer.MAX_VALUE) { return Integer.MAX_VALUE; } if (diff < Integer.MIN_VALUE) { return Integer.MIN_VALUE; } return (int)diff; } }); } /** Extract and return the numeric part of a filename. */ private long numberValue(String filename) { StringBuilder numberPart = new StringBuilder(); boolean foundFirstDigit = false; boolean done = false; for (int i = 0; i < filename.length() && !done; i++) { char ch = filename.charAt(i); if (Character.isDigit(ch)) { numberPart.append(ch); foundFirstDigit = true; } else if (foundFirstDigit) { // Found first non-digit after digits block - done. done = true; } } try { return Long.parseLong(numberPart.toString()); } catch (NumberFormatException e) { return -1L; } } }