blob: 2f7e1b31d49024952d3b50ab8b63c94e94606d9f [file] [log] [blame] [raw]
package li.cil.oc.server.fs
import java.io.{FileNotFoundException, IOException, InputStream}
import li.cil.oc.api
import li.cil.oc.api.fs.Mode
import net.minecraft.nbt.{NBTTagList, NBTTagCompound}
import scala.collection.mutable
abstract class InputStreamFileSystem extends api.FileSystem {
private val handles = mutable.Map.empty[Int, Handle]
protected def maxHandles = 32
def open(path: String, mode: Mode.Value) = if (mode == Mode.Read && exists(path) && !isDirectory(path)) {
if (handles.size >= maxHandles) throw new IOException("too many open files")
val handle = Iterator.continually((Math.random() * Int.MaxValue).toInt + 1).filterNot(handles.contains).next()
openInputStream(path, handle) match {
case Some(stream) =>
handles += handle -> new Handle(this, handle, path, stream)
handle
case _ => throw new FileNotFoundException()
}
} else throw new FileNotFoundException()
def file(handle: Int) = handles.get(handle): Option[api.fs.File]
def close() {
for (handle <- handles.values)
handle.close()
}
def load(nbt: NBTTagCompound) {
val handlesNbt = nbt.getTagList("handles")
(0 until handlesNbt.tagCount).map(handlesNbt.tagAt).map(_.asInstanceOf[NBTTagCompound]).foreach(handleNbt => {
val handle = handleNbt.getInteger("handle")
val path = handleNbt.getString("path")
val position = handleNbt.getLong("position")
openInputStream(path, handle) match {
case Some(stream) =>
val inputHandle = new Handle(this, handle, path, stream)
inputHandle.position = stream.skip(position) // May be != position if the file changed since we saved.
handles += handle -> inputHandle
case _ => // The source file seems to have changed since last time.
}
})
}
def save(nbt: NBTTagCompound) {
val handlesNbt = new NBTTagList()
for (file <- handles.values) {
assert(!file.isClosed)
val handleNbt = new NBTTagCompound()
handleNbt.setInteger("handle", file.handle)
handleNbt.setString("path", file.path)
handleNbt.setLong("position", file.position)
handlesNbt.appendTag(handleNbt)
}
nbt.setTag("handles", handlesNbt)
}
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 {
var isClosed = false
var position = 0L
def length = owner.size(path)
def close() = if (!isClosed) {
isClosed = true
owner.handles -= handle
stream.close()
}
def read(into: Array[Byte]) = {
val read = stream.read(into)
if (read >= 0)
position += read
read
}
def seek(to: Long) = {
stream.reset()
stream.skip(to)
}
def write(value: Array[Byte]) = throw new IOException("file is read-only")
}
}