/**
* eAdventure (formerly <e-Adventure> and <e-Game>) is a research project of the
* <e-UCM> research group.
*
* Copyright 2005-2010 <e-UCM> research group.
*
* You can access a list of all the contributors to eAdventure at:
* http://e-adventure.e-ucm.es/contributors
*
* <e-UCM> is a research group of the Department of Software Engineering
* and Artificial Intelligence at the Complutense University of Madrid
* (School of Computer Science).
*
* C Profesor Jose Garcia Santesmases sn,
* 28040 Madrid (Madrid), Spain.
*
* For more info please visit: <http://e-adventure.e-ucm.es> or
* <http://www.e-ucm.es>
*
* ****************************************************************************
*
* This file is part of eAdventure, version 2.0
*
* eAdventure is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* eAdventure 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with eAdventure. If not, see <http://www.gnu.org/licenses/>.
*/
package ead.importer;
import com.google.inject.Inject;
import ead.importer.interfaces.EAdElementFactory;
import ead.importer.interfaces.ResourceImporter;
import es.eucm.ead.importer.inputstreamcreators.ImporterInputStreamCreator;
import es.eucm.ead.model.elements.AdventureGame;
import es.eucm.ead.model.params.text.EAdString;
import es.eucm.ead.tools.StringHandler;
import es.eucm.ead.tools.java.reflection.JavaReflectionProvider;
import es.eucm.ead.writer.AdventureWriter;
import es.eucm.ead.writer.StringWriter;
import es.eucm.eadventure.common.data.adventure.AdventureData;
import es.eucm.eadventure.common.loader.InputStreamCreator;
import es.eucm.eadventure.common.loader.Loader;
import es.eucm.eadventure.common.loader.incidences.Incidence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* An importer for old games from 1.x version
*
*/
public class EAdventureImporter {
/**
* Sets if the importation must be done in debug mode. When this is
* activated, barrier, exits and active areas are not transparent
*/
public static final boolean IMPORTER_DEBUG = false;
public static final String CURRENT_EAD_ENGINE_VERSION = "ead-200";
private EAdElementImporter<AdventureData, AdventureGame> adventureImporter;
private ResourceImporter resourceImporter;
private InputStreamCreator inputStreamCreator;
private StringHandler stringsHandler;
private StringWriter stringFileHandler;
private EAdElementFactory elementFactory;
private String destinationFile;
private Random rand = new Random();
private List<ImporterProgressListener> listeners;
static private Logger logger = LoggerFactory
.getLogger(EAdventureImporter.class);
@Inject
public EAdventureImporter(
EAdElementImporter<AdventureData, AdventureGame> adventureImporter,
ResourceImporter resourceImporter,
InputStreamCreator inputStreamCreator,
StringHandler stringsHandler, StringWriter stringFileHandler,
EAdElementFactory elementFactory) {
this.adventureImporter = adventureImporter;
this.resourceImporter = resourceImporter;
this.inputStreamCreator = inputStreamCreator;
this.stringsHandler = stringsHandler;
this.stringFileHandler = stringFileHandler;
this.elementFactory = elementFactory;
this.listeners = new ArrayList<ImporterProgressListener>();
}
/**
* Imports and old game form 1.x version
*
* @param eadFile
* original ead file
* @param destination
* File where the import project will be stored. If {@code null},
* import project won't be saved
* @param format
* format for the output file. Can be "none" to make a folder
* with the game, or "ead" or "zip" to pack the game in a zip
* file
*/
public AdventureGame importGame(String eadFile, String destination,
String format) {
// Init importer
updateProgress(0, "Starting importer...");
stringsHandler.getStrings().clear();
((ImporterInputStreamCreator) inputStreamCreator).setFile(eadFile);
elementFactory.init();
boolean zipped = format.equals("ead") || format.equals("zip");
// Loading old game
updateProgress(10, "Loading old game...");
AdventureData adventureData = loadGame();
if (adventureData == null) {
updateProgress(100, "Error: model could not be loaded");
return null;
}
updateProgress(40, "Creating temporary files...");
// Set the destination folder (a temporary folder is used when the game
// is going to be zipped)
File destinationFolder = null;
if (zipped || destination == null) {
String tempFolder = System.getProperty("java.io.tmpdir");
File tmpDir = new File(tempFolder + File.separator
+ "eAdventureTemp" + rand.nextInt());
destinationFolder = tmpDir;
} else {
destinationFolder = new File(destination);
}
if (destinationFolder.exists() == false) {
destinationFolder.mkdir();
}
resourceImporter.setPath(destinationFolder.getAbsolutePath());
destinationFile = destinationFolder.getAbsolutePath();
updateProgress(50, "Converting old model...");
AdventureGame model = adventureImporter.init(adventureData);
model = adventureImporter.convert(adventureData, model);
updateProgress(90, "Creating " + destination);
createGameFile(model, destinationFolder.getAbsolutePath(), destination,
"." + format, "Imported version", zipped);
updateProgress(100, "Done.");
return model;
}
/**
* Creates a game file.
*
* @param model
* to save
* @param path
* to save it to
* @param destination
* file name within path
* @param forceExtension
* extension to set on destination; may be null
* @param propertiesComment
* comment to set on properties file
*/
public boolean createGameFile(AdventureGame model, String path,
String destination, String forceExtension,
String propertiesComment, boolean zipped) {
boolean ok = true;
// Create data.xml
AdventureWriter writer = new AdventureWriter(
new JavaReflectionProvider());
/* writer.write(model, path
+ (path.charAt(path.length() - 1) == '/' ? "" : "/")
+ "data.xml");*/
// Create strings.xml
File f = new File(path, "strings.xml");
try {
stringFileHandler.write(f.getAbsolutePath(), stringsHandler
.getStrings());
} catch (Exception e) {
logger.error("Error writing strings file while importing '{}'",
destination, e);
ok = false;
}
// ead.properties
File propertiesFile = new File(path, "ead.properties");
Properties properties = new Properties();
properties.setProperty("targetEngine", CURRENT_EAD_ENGINE_VERSION);
FileOutputStream output = null;
try {
output = new FileOutputStream(propertiesFile);
properties.store(output, propertiesComment);
} catch (Exception e) {
logger.error("Error writing properties file '{}'", propertiesFile
.getAbsolutePath(), e);
ok = false;
} finally {
if (output != null) {
try {
output.close();
} catch (IOException e) {
logger.error("Error writing properties file '{}'",
propertiesFile.getAbsolutePath(), e);
}
}
}
if (zipped) {
// Create final zip file with name destination+extension
// with everything (recursive) in 'path'
String fileName = (forceExtension == null || destination
.endsWith(forceExtension)) ? destination : destination
+ forceExtension;
File outputZipFile = new File(fileName);
ZipOutputStream out = null;
try {
out = new ZipOutputStream(new BufferedOutputStream(
new FileOutputStream(outputZipFile)));
addFolderToZip(out, new File(path), false);
logger.debug("Zip file {} complete", outputZipFile
.getAbsolutePath());
} catch (Exception e) {
logger.error("Error outputting zip to {}", destination, e);
ok = false;
} finally {
if (out != null) {
try {
out.close();
} catch (IOException ioe) {
logger.error(
"Could not close zip file writing to '{}'",
fileName, ioe);
}
}
}
}
return ok;
}
/**
* Adds all files in folder to the supplied zipOutputStream. Optionally
* includes their full paths too.
*
* @param zip
* destination stream
* @param folder
* folder to add
* @param addPath
* whether to include full path information or not
* @throws IOException
* if any error while adding
*/
public void addFolderToZip(ZipOutputStream zip, File folder, boolean addPath)
throws IOException {
logger.debug("adding folder {} to zip", folder);
if (folder == null || !folder.isDirectory()) {
throw new IllegalArgumentException("not a folder: " + folder);
}
byte data[] = new byte[1024];
File files[] = folder.listFiles();
for (File f : files) {
try {
if (f.isDirectory()) {
addFolderToZip(zip, f, true);
} else {
String entryName = (addPath ? f.getParentFile().getName()
+ "/" : "")
+ f.getName();
zip.putNextEntry(new ZipEntry(entryName));
BufferedInputStream in = new BufferedInputStream(
new FileInputStream(f), data.length);
int count;
while ((count = in.read(data, 0, data.length)) != -1) {
zip.write(data, 0, count);
}
zip.closeEntry();
in.close();
}
} catch (IOException e) {
logger.error("Error adding folder {} to zip", folder, e);
throw e;
}
}
}
/**
* Loads an old model AdventureData
*
*/
public AdventureData loadGame() {
ArrayList<Incidence> incidences = new ArrayList<Incidence>();
AdventureData data = null;
try {
data = Loader.loadAdventureData(inputStreamCreator, incidences);
} catch (Exception e) {
logger.error("Exception while reading old <e-Adventure> game", e);
}
if (data == null) {
logger.warn("Invalid <e-Adventure> game");
}
if (incidences.size() > 0) {
logger.info("There were the following incidences during loading:");
for (Incidence i : incidences) {
logger.info(i.getMessage());
}
}
return data;
}
public String getDestinationFile() {
return destinationFile;
}
public Map<EAdString, String> getStrings() {
return stringsHandler.getStrings();
}
public void addProgressListener(ImporterProgressListener progressListener) {
listeners.add(progressListener);
}
public void removeProgressListener(ImporterProgressListener progressListener) {
listeners.remove(progressListener);
}
public void updateProgress(int progress, String text) {
logger.debug("Importer progress update: {}", text);
for (ImporterProgressListener l : listeners) {
l.update(progress, text);
}
}
public static interface ImporterProgressListener {
// TODO It would be interesting to pass two values, current
// and max, so the progress bar can extrapolate between the
// two and give the appearance of work most of the time
public void update(int progress, String text);
}
public AdventureGame importGame(String absolutePath, String destination) {
return importGame(absolutePath, destination, "none");
}
}