329 lines
10 KiB
Java
329 lines
10 KiB
Java
package magic.utility;
|
|
|
|
import java.io.File;
|
|
import java.io.FileFilter;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.ObjectInputStream;
|
|
import java.io.ObjectOutputStream;
|
|
import java.nio.file.DirectoryStream;
|
|
import java.nio.file.FileVisitResult;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.nio.file.Paths;
|
|
import java.nio.file.SimpleFileVisitor;
|
|
import java.nio.file.attribute.BasicFileAttributes;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
|
|
import org.apache.commons.io.FileUtils;
|
|
import org.apache.commons.io.FilenameUtils;
|
|
|
|
import magic.data.GeneralConfig;
|
|
import magic.model.IRenderableCard;
|
|
import magic.model.MagicCardDefinition;
|
|
import magic.model.MagicGameLog;
|
|
|
|
/**
|
|
* Utility class for useful or common file-system related tasks.
|
|
*
|
|
*/
|
|
public final class MagicFileSystem {
|
|
|
|
private MagicFileSystem() {}
|
|
|
|
// card images
|
|
public static final String CARD_IMAGE_FOLDER = "cards";
|
|
public static final String TOKEN_IMAGE_FOLDER = "tokens";
|
|
private static final String CARD_IMAGE_EXT = ".jpg";
|
|
|
|
public enum ImagesPath {
|
|
|
|
CARDS(CARD_IMAGE_FOLDER),
|
|
TOKENS(TOKEN_IMAGE_FOLDER),
|
|
CUSTOM("custom"),
|
|
CROPS("crops");
|
|
|
|
private final String directoryName;
|
|
|
|
private ImagesPath(final String directoryName) {
|
|
this.directoryName = directoryName;
|
|
}
|
|
|
|
public Path getPath() {
|
|
return GeneralConfig.getInstance().getCardImagesPath().resolve(directoryName);
|
|
}
|
|
}
|
|
|
|
// Top level install directory containing exe, etc.
|
|
public static final Path INSTALL_PATH;
|
|
static {
|
|
if (System.getProperty("magarena.dir", "").isEmpty()) {
|
|
INSTALL_PATH = Paths.get(System.getProperty("user.dir"));
|
|
} else {
|
|
INSTALL_PATH = Paths.get(System.getProperty("magarena.dir"));
|
|
}
|
|
}
|
|
|
|
public static final String DATA_DIRECTORY_NAME = System.getProperty("data.dir", "Magarena");
|
|
private static final Path DATA_PATH = INSTALL_PATH.resolve(DATA_DIRECTORY_NAME);
|
|
|
|
public enum DataPath {
|
|
|
|
DECKS("decks"),
|
|
MODS("mods"),
|
|
SCRIPTS("scripts"),
|
|
SCRIPTS_MISSING("scripts_missing"),
|
|
SCRIPTS_ORIG("scripts_orig"),
|
|
LOGS("logs"),
|
|
DUELS("duels"),
|
|
PLAYERS("players"),
|
|
AVATARS("avatars"),
|
|
FIREMIND("firemind"),
|
|
SAVED_GAMES("saved_games"),
|
|
TRANSLATIONS("translations"),
|
|
IMAGES("images"),
|
|
REPORTS("reports"),
|
|
THEMES("themes"),
|
|
STATS("stats")
|
|
;
|
|
|
|
private final Path directoryPath;
|
|
|
|
private DataPath(final String directoryName) {
|
|
directoryPath = DATA_PATH.resolve(directoryName);
|
|
MagicFileSystem.verifyDirectoryPath(directoryPath);
|
|
}
|
|
|
|
public Path getPath() {
|
|
return directoryPath;
|
|
}
|
|
|
|
}
|
|
|
|
private static final FileFilter THEME_FILE_FILTER = (final File file) ->
|
|
file.isDirectory() || (file.isFile() && file.getName().endsWith(".zip"));
|
|
|
|
/**
|
|
* Returns the main data directory.
|
|
* <p>
|
|
* Generally, this will contain sub-directories for the
|
|
* different categories of data that can be generated.
|
|
*/
|
|
public static Path getDataPath() {
|
|
return DATA_PATH;
|
|
}
|
|
|
|
/**
|
|
* Returns a pre-defined data sub-directory of main data path.
|
|
*/
|
|
public static Path getDataPath(final DataPath directory) {
|
|
return directory.getPath();
|
|
}
|
|
|
|
public static Path getImagesPath(final ImagesPath imageType) {
|
|
return imageType.getPath();
|
|
}
|
|
|
|
private static String getImageFilename(final MagicCardDefinition card) {
|
|
return card.getImageName() + CARD_IMAGE_EXT;
|
|
}
|
|
|
|
/**
|
|
* Returns a File object representing the given card's image file.
|
|
*/
|
|
public static File getPrintedCardImage(final MagicCardDefinition card) {
|
|
final Path imageDirectory = card.isToken() ?
|
|
getImagesPath(ImagesPath.TOKENS) :
|
|
getImagesPath(ImagesPath.CARDS);
|
|
return new File(imageDirectory.toFile(), getImageFilename(card));
|
|
}
|
|
|
|
public static File getPrintedCardImage(IRenderableCard face) {
|
|
return getPrintedCardImage(face.getCardDefinition());
|
|
}
|
|
|
|
/**
|
|
* Returns a File object representing the given card's cropped image file.
|
|
*/
|
|
public static File getCroppedCardImage(final IRenderableCard cardDef) {
|
|
final Path imageDirectory = getImagesPath(ImagesPath.CROPS);
|
|
return new File(imageDirectory.toFile(), cardDef.getImageName() + ".jpg");
|
|
}
|
|
|
|
/**
|
|
* Deletes all directory contents and then directory itself.
|
|
*/
|
|
public static void deleteDirectory(final Path root) {
|
|
if (Files.exists(root) == false) {
|
|
return;
|
|
}
|
|
try {
|
|
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
|
|
@Override
|
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
|
Files.delete(file);
|
|
return FileVisitResult.CONTINUE;
|
|
}
|
|
@Override
|
|
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
|
if (exc == null){
|
|
Files.delete(dir);
|
|
return FileVisitResult.CONTINUE;
|
|
}
|
|
throw exc;
|
|
}
|
|
});
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
|
|
public static void serializeStringList(final List<String> list, final File targetFile) {
|
|
try (final FileOutputStream fos = new FileOutputStream(targetFile);
|
|
final ObjectOutputStream oos = new ObjectOutputStream(fos)) {
|
|
oos.writeObject(list);
|
|
} catch (IOException ex) {
|
|
throw new RuntimeException(ex);
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
public static List<String> deserializeStringList(final File sourceFile) {
|
|
try (final FileInputStream fis = new FileInputStream(sourceFile);
|
|
final ObjectInputStream ois = new ObjectInputStream(fis)) {
|
|
return (List<String>)ois.readObject();
|
|
} catch (IOException|ClassNotFoundException ex) {
|
|
throw new RuntimeException(ex);
|
|
}
|
|
}
|
|
|
|
public static void verifyDirectoryPath(final Path path) {
|
|
if (!path.toFile().exists()) {
|
|
try {
|
|
Files.createDirectory(path);
|
|
} catch (IOException ex) {
|
|
throw new RuntimeException(String.format("Failed to create '%s'.", path), ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static File[] getSortedScriptFiles(final File scriptsDirectory) {
|
|
final File[] files = scriptsDirectory.listFiles(
|
|
(dir, name) -> name.toLowerCase(Locale.ENGLISH).endsWith(".txt")
|
|
);
|
|
Arrays.sort(files);
|
|
return files;
|
|
}
|
|
|
|
public static List<String> getTranslationFilenames() {
|
|
final List<String> filenames = new ArrayList<>();
|
|
final Path langPath = getDataPath(DataPath.TRANSLATIONS);
|
|
try (DirectoryStream<Path> ds = Files.newDirectoryStream(langPath, "*.txt")) {
|
|
for (Path p : ds) {
|
|
filenames.add(FilenameUtils.getBaseName(p.getFileName().toString()));
|
|
}
|
|
filenames.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
|
|
} catch (IOException ex) {
|
|
System.err.println(ex);
|
|
}
|
|
return filenames;
|
|
}
|
|
|
|
public static void deleteGeneralConfigFile() {
|
|
getDataPath().resolve(GeneralConfig.CONFIG_FILENAME).toFile().delete();
|
|
}
|
|
|
|
/**
|
|
* Should return the directory containing the current installation of Magarena.
|
|
* <p>
|
|
* The idea being that a new version of Magarena would most likely be
|
|
* installed to a new directory at the same level as the previous version,
|
|
* so it would display the previous version all ready to select & import.
|
|
*/
|
|
public static Path getDefaultImportDirectory() {
|
|
final Path p = getDataPath().getParent().getParent();
|
|
if (p == null) {
|
|
// triggered if using a single relative path for -Dmagarena.dir.
|
|
return getDataPath().getParent();
|
|
}
|
|
return p;
|
|
}
|
|
|
|
/**
|
|
* Confirms if two paths point to the same location regardless of whether
|
|
* the path is relative or absolute.
|
|
*/
|
|
public static boolean isSamePath(Path p1, Path p2) {
|
|
return p1.toAbsolutePath().equals(p2.toAbsolutePath());
|
|
}
|
|
|
|
/**
|
|
* Determines whether p2 is the same as or a sub-directory of p1.
|
|
*/
|
|
public static boolean directoryContains(Path p1, Path p2) {
|
|
if (p1 == null || p2 == null) {
|
|
return false;
|
|
}
|
|
if (isSamePath(p1, p2)) {
|
|
return true;
|
|
} else {
|
|
return directoryContains(p1, p2.getParent());
|
|
}
|
|
}
|
|
|
|
public static Path getCustomImagesPath() {
|
|
return getImagesPath(ImagesPath.CUSTOM);
|
|
}
|
|
|
|
public static Path getGameplayReportDirectory() {
|
|
return getDataPath(DataPath.REPORTS).resolve("gameplay");
|
|
}
|
|
|
|
public static void clearGameplayReportDirectory() throws IOException {
|
|
verifyDirectoryPath(getGameplayReportDirectory());
|
|
FileUtils.cleanDirectory(getGameplayReportDirectory().toFile());
|
|
}
|
|
|
|
public static File[] getThemes() {
|
|
return getDataPath(DataPath.THEMES).toFile().listFiles(THEME_FILE_FILTER);
|
|
}
|
|
|
|
public static Path getThemesPath() {
|
|
return getDataPath(DataPath.THEMES);
|
|
}
|
|
|
|
public static Path getBackgroundImagePath() {
|
|
return getDataPath(DataPath.MODS).resolve("background.image");
|
|
}
|
|
|
|
public static File getBackgroundImageFile() {
|
|
return getBackgroundImagePath().toFile();
|
|
}
|
|
|
|
public static Path getGameLogPath() {
|
|
return getDataPath(DataPath.LOGS).resolve(MagicGameLog.LOG_FILE);
|
|
}
|
|
|
|
public static boolean isMissingOrEmpty(Path path) throws IOException {
|
|
if (!Files.exists(path)) {
|
|
return true;
|
|
}
|
|
if (Files.isDirectory(path)) {
|
|
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(path)) {
|
|
return !dirStream.iterator().hasNext();
|
|
}
|
|
} else {
|
|
throw new IOException("Specified path is not a valid directory : " + path);
|
|
}
|
|
}
|
|
|
|
public static File getTranslationFile(String lang) {
|
|
return getDataPath(DataPath.TRANSLATIONS).resolve(lang + ".txt").toFile();
|
|
}
|
|
}
|