diff --git a/li/cil/oc/api/FileSystem.scala b/li/cil/oc/api/FileSystem.scala index 9274183f6..40fb326fe 100644 --- a/li/cil/oc/api/FileSystem.scala +++ b/li/cil/oc/api/FileSystem.scala @@ -1,17 +1,18 @@ package li.cil.oc.api import li.cil.oc.api.detail.FileSystemAPI -import li.cil.oc.api.fs.{Mode, File} +import li.cil.oc.api.fs.{Mode, Handle} import li.cil.oc.api.network.Node -import scala.language.reflectiveCalls /** - * Interface for disk driver compatible file systems. + * Interface for file system driver compatible file systems. *
- * To get a network node wrapping a file system and using the default disk - * driver, use `Filesystem.asNode`. To create a file system from a JAR file - * (for example the one containing your mod) use `Filesystem.fromClass`, - * providing a class from your mod as the first parameter. + * To create a file system from a JAR file or folder (in read-only mode; for + * example the one containing your mod) use `Filesystem.fromClass`, providing + * a class from your mod as the first parameter. + * + * To get a network node wrapping a file system and using the default file + * system driver, use `Filesystem.asNode`. * * Alternatively to using the factory methods for file systems in `Filesystem` * you are free to implement this interface yourself. @@ -30,10 +31,10 @@ trait FileSystem extends Persistable { /** * Gets the size of a file or folder. * - * For files this should return actual length of the file, in bytes. For + * For files this should return the actual length of the file, in bytes. For * folders it is recommended to return some value larger than zero to - * simulate the node cost (and for writable file systems to avoid users - * spawning an infinite amount of folders). + * simulate the node cost (in particular for writable file systems to avoid + * users spawning an infinite amount of folders). * * If the path is invalid this should return zero. It should never throw. * @@ -63,7 +64,7 @@ trait FileSystem extends Persistable { * * Sub-folders should be returned with a trailing slash, to indicate that * they are folders. This is primarily intended to avoid Lua programs having - * to check which of the entries are folders via calling `isFolder`, which + * to check which of the entries are folders via calling `isDirectory`, which * would be excruciatingly slow (since each call takes one game tick). * * If the folder is empty this should return an empty array. @@ -90,6 +91,17 @@ trait FileSystem extends Persistable { */ def remove(path: String): Boolean = false + /** + * Moves / renames a file or folder. + * + * This is only available for writable file systems. For read-only systems + * it should just always return false. + * + * @param from the name of the file or folder to move. + * @param to the location to move the file or folder to. + * @return true if the object was renamed; false otherwise. + * @throws FileNotFoundException if the source is not a file or folder. + */ def rename(from: String, to: String): Boolean = false /** @@ -122,7 +134,7 @@ trait FileSystem extends Persistable { * @param handle the ID of the handle to get the wrapper for. * @return the wrapper for that handle ID; None if the ID is invalid. */ - def file(handle: Int): Option[File] + def file(handle: Int): Option[Handle] /** * Called when the file system is deconstructed. @@ -142,10 +154,19 @@ trait FileSystem extends Persistable { object FileSystem extends FileSystemAPI { /** - * Creates a new file system based on a JAR file containing a class. + * Creates a new file system based on the location of a class. * * This can be used to wrap a folder in the assets folder of your mod's JAR. * The actual path is built like this: `"/assets/" + domain + "/" + root`. + * + * If the class is located in a JAR file, this will create a read-only file + * system based on that JAR file. If the class file is located in the native + * file system, this will create a read-only file system first trying from + * the actual location of the class file, and failing that by searching the + * class path (i.e. it'll look for a path constructed as described above). + * + * If the specified path cannot be located, the creation fails and this + * returns `None`. * * @param clazz the class whose containing JAR to wrap. * @param domain the mod domain, usually its name. @@ -157,7 +178,7 @@ object FileSystem extends FileSystemAPI { /** * Creates a network node that makes the specified file system available via - * the common disk driver. + * the common file system driver. * * This can be useful for providing some data if you don't wish to implement * your own driver. Which will probably be most of the time. If you need diff --git a/li/cil/oc/api/Network.scala b/li/cil/oc/api/Network.scala index 1a025636d..f3120d043 100644 --- a/li/cil/oc/api/Network.scala +++ b/li/cil/oc/api/Network.scala @@ -37,11 +37,11 @@ import net.minecraft.world.IBlockAccess * nodes of the other network(s), when a network is split (all pairs). * * - * IMPORTANT: do not implement this interface yourself and create + * IMPORTANT: do *not* implement this interface yourself and create * instances of your own network implementation; this will lead to * incompatibilities with the built-in network implementation (which can only * merge with other networks of its own type). Always use the methods provided - * in Network to create and join networks. + * in `Network` to create and join networks. */ trait Network { /** diff --git a/li/cil/oc/api/driver/Slot.scala b/li/cil/oc/api/driver/Slot.scala index 44e1d4ae8..81f0ac7af 100644 --- a/li/cil/oc/api/driver/Slot.scala +++ b/li/cil/oc/api/driver/Slot.scala @@ -3,8 +3,8 @@ package li.cil.oc.api.driver /** * List of possible item component types. * - * This is used to determine which item components may go into which slots in a - * computer, since unlike block components, item components must be placed + * This is used to determine which item components may go into which slots in + * a computer, since unlike block components, item components must be placed * inside the computer, not next to it. */ object Slot extends Enumeration { diff --git a/li/cil/oc/api/fs/File.scala b/li/cil/oc/api/fs/Handle.scala similarity index 64% rename from li/cil/oc/api/fs/File.scala rename to li/cil/oc/api/fs/Handle.scala index d015216ce..4b4c3b1fe 100644 --- a/li/cil/oc/api/fs/File.scala +++ b/li/cil/oc/api/fs/Handle.scala @@ -3,29 +3,39 @@ package li.cil.oc.api.fs /** * Represents a handle to a file opened from a `FileSystem`. */ -trait File { +trait Handle { + /** The current position in the file. */ def position: Long + /** The total length of the file. */ def length: Long /** - * Closes the underlying handle. + * Closes the handle. * - * Any future calls to `read` or `write` should throw an `IOException` after - * this function was called. + * For example, if there is an underlying stream, this should close that + * stream. Any future calls to `read` or `write` should throw an + * `IOException` after this function was called. */ def close() /** - * Tries to read as much data from the file as fits into the specified array. + * Tries to read as much data from the file as fits into the specified + * array. * * @param into the buffer to read the data into. - * @return the number of bytes read; -1 if there are no more bytes. + * @return the number of bytes read; -1 if there are no more bytes (EOF). * @throws IOException if the file was opened in writing mode or an I/O * error occurred or the file was already closed. */ def read(into: Array[Byte]): Int + /** + * Jump to the specified position in the file, if possible. + * + * @param to the position in the file to jump to. + * @return the resulting position in the file. + */ def seek(to: Long): Long /** diff --git a/li/cil/oc/api/fs/Mode.scala b/li/cil/oc/api/fs/Mode.scala index e95993d92..9ed963524 100644 --- a/li/cil/oc/api/fs/Mode.scala +++ b/li/cil/oc/api/fs/Mode.scala @@ -1,10 +1,27 @@ package li.cil.oc.api.fs +/** + * Possible file modes. + * + * This is used when opening files from a `FileSystem`. + */ object Mode extends Enumeration { + /** Open a file in reading mode. */ val Read = Value("Read") + + /** Open a file in writing mode, overwriting existing contents. */ val Write = Value("Write") + + /** Open a file in append mode, writing new data after existing contents. */ val Append = Value("Append") + /** + * Parses a mode from a string. + * + * @param value the string to parse. + * @return the mode the string represents. + * @throws IllegalArgumentException if the string cannot be parsed to a mode. + */ def parse(value: String) = value match { case "r" | "rb" => Read case "w" | "wb" => Write diff --git a/li/cil/oc/server/fs/InputStreamFileSystem.scala b/li/cil/oc/server/fs/InputStreamFileSystem.scala index 2f7e1b31d..4f0abdeb2 100644 --- a/li/cil/oc/server/fs/InputStreamFileSystem.scala +++ b/li/cil/oc/server/fs/InputStreamFileSystem.scala @@ -22,7 +22,7 @@ abstract class InputStreamFileSystem extends api.FileSystem { } } else throw new FileNotFoundException() - def file(handle: Int) = handles.get(handle): Option[api.fs.File] + def file(handle: Int) = handles.get(handle): Option[api.fs.Handle] def close() { for (handle <- handles.values) @@ -60,7 +60,7 @@ abstract class InputStreamFileSystem extends api.FileSystem { protected def openInputStream(path: String, handle: Long): Option[InputStream] - protected class Handle(val owner: InputStreamFileSystem, val handle: Int, val path: String, val stream: InputStream) extends api.fs.File { + protected class Handle(val owner: InputStreamFileSystem, val handle: Int, val path: String, val stream: InputStream) extends api.fs.Handle { var isClosed = false var position = 0L