package electrosphere.util; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import electrosphere.game.data.creature.type.ai.AITreeData; import electrosphere.game.data.creature.type.ai.AITreeDataSerializer; import electrosphere.game.data.creature.type.movement.MovementSystem; import electrosphere.game.data.creature.type.movement.MovementSystemSerializer; import electrosphere.logger.LoggerInterface; import electrosphere.server.macro.character.CharacterData; import electrosphere.server.macro.character.CharacterDataSerializer; import electrosphere.server.physics.terrain.generation.noise.NoiseModuleSerializer; import electrosphere.server.physics.terrain.generation.noise.NoiseSampler; import electrosphere.util.annotation.AnnotationExclusionStrategy; import java.awt.image.BufferedImage; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectOutputStream; import java.io.Serializable; import java.nio.ByteBuffer; import java.nio.channels.SeekableByteChannel; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.LinkedList; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import javax.imageio.ImageIO; import org.lwjgl.BufferUtils; /** * Utilities for dealing with files */ public class FileUtils { /** * Creates the gson instance */ static { //init gson GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(MovementSystem.class, new MovementSystemSerializer()); gsonBuilder.registerTypeAdapter(AITreeData.class, new AITreeDataSerializer()); gsonBuilder.registerTypeAdapter(NoiseSampler.class, new NoiseModuleSerializer()); gsonBuilder.registerTypeAdapter(CharacterData.class, new CharacterDataSerializer()); gsonBuilder.addDeserializationExclusionStrategy(new AnnotationExclusionStrategy()); gsonBuilder.addSerializationExclusionStrategy(new AnnotationExclusionStrategy()); gson = gsonBuilder.create(); //init gamedir gameDir = Paths.get("").toFile(); } //used for serialization/deserialization in file operations static Gson gson; //the game's root directory static File gameDir = null; //maximum number of attempt to read the file static final int maxReadFails = 3; //Timeout duration between read attempts static final int READ_TIMEOUT_DURATION = 5; /** * Reads a file to a string * @param f The file * @return The string */ public static String readFileToString(File f){ String rVal = ""; BufferedReader reader; try { reader = Files.newBufferedReader(f.toPath()); int failCounter = 0; boolean reading = true; StringBuilder builder = new StringBuilder(""); while(reading){ if(reader.ready()){ failCounter = 0; int nextValue = reader.read(); if(nextValue == -1){ reading = false; } else { builder.append((char)nextValue); } } else { failCounter++; if(failCounter > maxReadFails){ reading = false; } else { try { TimeUnit.MILLISECONDS.sleep(READ_TIMEOUT_DURATION); } catch (InterruptedException ex) { ex.printStackTrace(); } } } } rVal = builder.toString(); } catch (IOException ex) { ex.printStackTrace(); } return rVal; } public static String readStreamToString(InputStream resourceInputStream){ String rVal = ""; BufferedReader reader; try { reader = new BufferedReader(new InputStreamReader(resourceInputStream)); int failCounter = 0; boolean reading = true; StringBuilder builder = new StringBuilder(""); while(reading){ if(reader.ready()){ failCounter = 0; int nextValue = reader.read(); if(nextValue == -1){ reading = false; } else { builder.append((char)nextValue); } } else { failCounter++; if(failCounter > maxReadFails){ reading = false; } else { try { TimeUnit.MILLISECONDS.sleep(READ_TIMEOUT_DURATION); } catch (InterruptedException ex) { ex.printStackTrace(); } } } } rVal = builder.toString(); } catch (IOException ex) { ex.printStackTrace(); } return rVal; } /** * Sanitizes a relative file path, guaranteeing that the initial slash is correct * @param filePath The raw file path * @return The sanitized file path */ public static String sanitizeFilePath(String filePath){ String rVal = new String(filePath); rVal = rVal.trim(); if(rVal.startsWith("./")){ return rVal; } else if(!rVal.startsWith("/")){ rVal = "/" + rVal; } return rVal; } /** * Serializes an object to a filepath * @param filePath The filepath * @param object The object */ public static void serializeObjectToFilePath(String filePath, Object object){ Path path = new File(filePath).toPath(); try { Files.write(path, gson.toJson(object).getBytes()); } catch (IOException ex) { LoggerInterface.loggerFileIO.ERROR(filePath, ex); } } /** * Serializes an object to a filepath * @param file The file to save to * @param object The object */ public static void serializeObjectToFilePath(File file, Object object){ Path path = file.toPath(); try { Files.write(path, gson.toJson(object).getBytes()); } catch (IOException ex) { LoggerInterface.loggerFileIO.ERROR(file.getAbsolutePath(), ex); } } /** * Gets an assets file * @param pathName The relative path in the assets folder * @return The file */ public static File getAssetFile(String pathName){ String sanitizedFilePath = sanitizeFilePath(pathName); File targetFile = new File("./assets" + sanitizedFilePath); return targetFile; } /** * Gets an assets file as a byte buffer * @param pathName The relative path in the assets folder * @return The byte buffer containing the contents of the asset file * @throws IOException Thrown if there are any io issues */ public static ByteBuffer getAssetFileAsByteBuffer(String pathName) throws IOException{ String sanitizedFilePath = sanitizeFilePath(pathName); File targetFile = new File("./assets" + sanitizedFilePath); ByteBuffer buff = null; try(SeekableByteChannel byteChannel = Files.newByteChannel(targetFile.toPath())){ buff = BufferUtils.createByteBuffer((int)(byteChannel.size() + 1)); while(byteChannel.read(buff) != -1){ } buff.flip(); } return buff; } /** * Gets a cache file * @param pathName The relative path in the cache folder * @return The file */ public static File getCacheFile(String pathName){ String sanitizedFilePath = sanitizeFilePath(pathName); File targetFile = new File("./.cache" + sanitizedFilePath); return targetFile; } /** * Gets a save file * @param saveName The name of the save * @param pathName The relative path in the save's folder * @return the file */ public static File getSaveFile(String saveName, String pathName){ String sanitizedFilePath = sanitizeFilePath(pathName); String fullPath = "./saves/" + saveName + "/" + sanitizedFilePath; File targetFile = new File(fullPath); return targetFile; } /** * Gets an asset file as a stream * @param pathName The path of the file * @return The stream * @throws IOException Thrown if Files fails to create the stream */ public static InputStream getAssetFileAsStream(String pathName) throws IOException{ String sanitizedFilePath = sanitizeFilePath(pathName); File targetFile = new File("./assets" + sanitizedFilePath); return Files.newInputStream(targetFile.toPath()); } /** * Gets an asset file as a string * @param pathName The path of the file * @return The string * @throws IOException Thrown if Files fails to read the file */ public static String getAssetFileAsString(String pathName) throws IOException{ String sanitizedFilePath = sanitizeFilePath(pathName); File targetFile = new File("./assets" + sanitizedFilePath); return Files.readString(targetFile.toPath()); } /** * Loads an object from the assets folder * @param The type of object * @param pathName The relative path to the file * @param className The class of the object inside the file * @return The file */ public static T loadObjectFromAssetPath(String pathName, Class className){ T rVal = null; String sanitizedFilePath = sanitizeFilePath(pathName); try { rVal = gson.fromJson(Files.newBufferedReader(getAssetFile(sanitizedFilePath).toPath()), className); } catch (IOException ex) { LoggerInterface.loggerFileIO.ERROR(ex); } return rVal; } /** * Loads an object from the assets folder * @param The type of object * @param file The file to load from * @param className The class of the object inside the file * @return The file */ public static T loadObjectFromFile(File file, Class className){ T rVal = null; try { rVal = gson.fromJson(Files.newBufferedReader(file.toPath()), className); } catch (IOException ex) { LoggerInterface.loggerFileIO.ERROR(ex); } return rVal; } /** * Gets an sql script file as a string * @param pathName The path of the sql script * @return The file's contents as a string * @throws IOException Thrown if the engine fails to read the file */ public static String getSQLScriptFileAsString(String pathName) throws IOException { String sanitizedFilePath = sanitizeFilePath(pathName); File targetFile = new File("./assets/DB/" + sanitizedFilePath); return Files.readString(targetFile.toPath()); } /** * Loads an object from a save folder * @param The type of the object * @param saveName The name of the save * @param pathName The path of the file containing the object json * @param className The class of the object * @return The object */ public static T loadObjectFromSavePath(String saveName, String pathName, Class className){ T rVal = null; String sanitizedFilePath = sanitizeFilePath(pathName); try { rVal = gson.fromJson(Files.newBufferedReader(getSaveFile(saveName,sanitizedFilePath).toPath()), className); } catch (IOException ex) { ex.printStackTrace(); } return rVal; } /** * Serializes an object to a save folder * @param saveName The name of the save * @param pathName The path within the save folder to the file * @param object The object to save */ public static void serializeObjectToSavePath(String saveName, String pathName, Object object){ String sanitizedFilePath = sanitizeFilePath(pathName); try { Files.write(getSaveFile(saveName,sanitizedFilePath).toPath(), gson.toJson(object).getBytes()); } catch (IOException ex) { ex.printStackTrace(); } } /** * Reads a binary file as an array of bytes * @param saveName The save name * @param pathName The path within the save folder * @return The array of bytes */ public static byte[] loadBinaryFromSavePath(String saveName, String pathName){ byte[] rVal = null; String sanitizedFilePath = sanitizeFilePath(pathName); try { rVal = Files.readAllBytes(getSaveFile(saveName,sanitizedFilePath).toPath()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return rVal; } /** * Writes a binary file to a save folder's file * @param saveName The name of the save * @param pathName The path within the save folder * @param data The data to write */ public static void saveBinaryToSavePath(String saveName, String pathName, byte[] data){ String sanitizedFilePath = sanitizeFilePath(pathName); try { Files.write(getSaveFile(saveName,sanitizedFilePath).toPath(), data); } catch (IOException ex) { ex.printStackTrace(); } } /** * Checks if a given file exists in a given save * @param saveName the name of the save * @param filePath The path to the file RELATIVE TO THE SAVE DIRECTORY * @return true if it exists, false otherwise */ public static boolean checkSavePathExists(String saveName, String filePath){ String sanitizedFilePath = sanitizeFilePath(filePath); return getSaveFile(saveName,sanitizedFilePath).exists(); } /** * Checks if a directory exists * @param fileName * @return true if directory exists, false otherwise */ public static boolean checkFileExists(String fileName){ File targetDir = new File(sanitizeFilePath(fileName)); if(targetDir.exists()){ return true; } else { return false; } } /** * Trys to create a directory * @param directoryName * @return true if directory was created, false if it was not */ public static boolean createDirectory(String directoryName){ String sanitizedPath = sanitizeFilePath(directoryName); File targetDir = new File(sanitizedPath); if(targetDir.exists()){ return false; } else { return targetDir.mkdirs(); } } /** * Lists the files in a directory * @param directoryName The path of the directory * @return A list containing the names of all files inside that directory */ public static List listDirectory(String directoryName){ List rVal = new LinkedList(); String sanitizedPath = sanitizeFilePath(directoryName); File targetDir = new File(sanitizedPath); String[] files = targetDir.list(); for(String name : files){ rVal.add(name); } return rVal; } /** * Recursively deletes a path * @param path The path */ public static void recursivelyDelete(String path){ File file = new File(path); if(file.isDirectory()){ for(File child : file.listFiles()){ recursivelyDelete(child.getAbsolutePath()); } } if(file.exists()){ try { Files.delete(file.toPath()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * Writes a buffered image to a given path * @param image The image * @param path The path */ public static void writeBufferedImage(BufferedImage image, String path){ //write the buffered image out try { File outputFile = FileUtils.getAssetFile(path); if(!outputFile.getParentFile().exists()){ outputFile.getParentFile().mkdirs(); } if(!outputFile.exists()){ outputFile.createNewFile(); } ImageIO.write(image,"png",outputFile); } catch (IOException e) { LoggerInterface.loggerRenderer.ERROR(e); } } /** * Gets a file's path relative to a given directory * @param file The file * @param directory The directory * @return The relative path */ public static String relativize(File file, File directory){ return directory.toURI().relativize(file.toURI()).getPath(); } /** * Computes the checksum of an object * @param object The object * @return The checksum * @throws IOException Thrown on io errors reading the file * @throws NoSuchAlgorithmException Thrown if MD5 isn't supported */ public static String getChecksum(Serializable object) throws IOException, NoSuchAlgorithmException { ByteArrayOutputStream baos = null; ObjectOutputStream oos = null; try { baos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(baos); oos.writeObject(object); MessageDigest md = MessageDigest.getInstance("MD5"); byte[] bytes = md.digest(baos.toByteArray()); StringBuffer builder = new StringBuffer(); for(byte byteCurr : bytes){ builder.append(String.format("%02x",byteCurr)); } return builder.toString(); } finally { oos.close(); baos.close(); } } /** * Writes a ByteBuffer to a file and compresses it * @param file The file * @param buff The buffer */ public static void writeBufferToCompressedFile(File file, ByteBuffer buff) throws IOException { try (GZIPOutputStream outStream = new GZIPOutputStream(Files.newOutputStream(file.toPath()))){ outStream.write(buff.array()); } } /** * Writes a ByteBuffer to a file and compresses it * @param file The file * @param buff The buffer */ public static ByteBuffer readBufferFromCompressedFile(File file) throws IOException { ByteBuffer buff = null; try (GZIPInputStream inStream = new GZIPInputStream(Files.newInputStream(file.toPath()))){ byte[] bytes = inStream.readAllBytes(); buff = ByteBuffer.allocate(bytes.length); buff.put(bytes); buff.flip(); } return buff; } }