mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-16 10:51:55 -04:00
fixed / properly implemented zip file system
This commit is contained in:
parent
33a45493b8
commit
6aa2bd8298
@ -2,7 +2,7 @@ package li.cil.oc.server.fs
|
|||||||
|
|
||||||
import dan200.computer.api.{IWritableMount, IMount}
|
import dan200.computer.api.{IWritableMount, IMount}
|
||||||
import java.io
|
import java.io
|
||||||
import java.util.zip.ZipFile
|
import java.net.URL
|
||||||
import li.cil.oc.api.fs.Label
|
import li.cil.oc.api.fs.Label
|
||||||
import li.cil.oc.server.component
|
import li.cil.oc.server.component
|
||||||
import li.cil.oc.{Settings, api}
|
import li.cil.oc.{Settings, api}
|
||||||
@ -10,33 +10,45 @@ import net.minecraftforge.common.DimensionManager
|
|||||||
|
|
||||||
object FileSystem extends api.detail.FileSystemAPI {
|
object FileSystem extends api.detail.FileSystemAPI {
|
||||||
override def fromClass(clazz: Class[_], domain: String, root: String): api.fs.FileSystem = {
|
override def fromClass(clazz: Class[_], domain: String, root: String): api.fs.FileSystem = {
|
||||||
val codeSource = clazz.getProtectionDomain.getCodeSource
|
val innerPath = ("/assets/" + domain + "/" + (root.trim + "/")).replace("//", "/")
|
||||||
if (codeSource == null) return null
|
|
||||||
val file = new io.File(codeSource.getLocation.toURI)
|
val codeSource = clazz.getProtectionDomain.getCodeSource.getLocation.getPath
|
||||||
if (!file.exists || file.isDirectory) return null
|
val (codeUrl, isArchive) =
|
||||||
val path = ("/assets/" + domain + "/" + (root.trim + "/")).replace("//", "/")
|
if (codeSource.contains(".zip!") || codeSource.contains(".jar!"))
|
||||||
if (file.getName.endsWith(".class"))
|
(codeSource.substring(0, codeSource.lastIndexOf('!')), true)
|
||||||
new io.File(new io.File(file.getParent), path) match {
|
else
|
||||||
|
(codeSource, false)
|
||||||
|
|
||||||
|
val file = try {
|
||||||
|
val url = new URL(codeUrl)
|
||||||
|
try {
|
||||||
|
new io.File(url.toURI)
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
case _: Throwable => new io.File(url.getPath)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
case _: Throwable => new io.File(codeSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isArchive) {
|
||||||
|
ZipFileInputStreamFileSystem.fromFile(file, innerPath.substring(1))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!file.exists || file.isDirectory) return null
|
||||||
|
new io.File(new io.File(file.getParent), innerPath) match {
|
||||||
case fsp if fsp.exists() && fsp.isDirectory =>
|
case fsp if fsp.exists() && fsp.isDirectory =>
|
||||||
new ReadOnlyFileSystem(fsp)
|
new ReadOnlyFileSystem(fsp)
|
||||||
case _ =>
|
case _ =>
|
||||||
System.getProperty("java.class.path").split(System.getProperty("path.separator")).
|
System.getProperty("java.class.path").split(System.getProperty("path.separator")).
|
||||||
find(cp => {
|
find(cp => {
|
||||||
val fsp = new io.File(new io.File(cp), path)
|
val fsp = new io.File(new io.File(cp), innerPath)
|
||||||
fsp.exists() && fsp.isDirectory
|
fsp.exists() && fsp.isDirectory
|
||||||
}) match {
|
}) match {
|
||||||
case None => null
|
case None => null
|
||||||
case Some(dir) => new ReadOnlyFileSystem(new io.File(new io.File(dir), path))
|
case Some(dir) => new ReadOnlyFileSystem(new io.File(new io.File(dir), innerPath))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
val zip = new ZipFile(file)
|
|
||||||
val entry = zip.getEntry(path)
|
|
||||||
if (entry == null || !entry.isDirectory) {
|
|
||||||
zip.close()
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
new ZipFileInputStreamFileSystem(zip, path)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,24 +1,28 @@
|
|||||||
package li.cil.oc.server.fs
|
package li.cil.oc.server.fs
|
||||||
|
|
||||||
|
import com.google.common.cache.CacheBuilder
|
||||||
|
import java.io
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.util.concurrent.Callable
|
||||||
|
import java.util.logging.Level
|
||||||
import java.util.zip.{ZipEntry, ZipFile}
|
import java.util.zip.{ZipEntry, ZipFile}
|
||||||
|
import li.cil.oc.OpenComputers
|
||||||
|
import li.cil.oc.server.fs.ZipFileInputStreamFileSystem.{ArchiveFile, ArchiveDirectory}
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
import scala.language.postfixOps
|
||||||
|
|
||||||
// TODO we may want to read in the complete zip file (and keep a cache in the
|
class ZipFileInputStreamFileSystem(private val archive: ArchiveDirectory) extends InputStreamFileSystem {
|
||||||
// factory) to avoid a ton of open real file handles.
|
|
||||||
class ZipFileInputStreamFileSystem(val zip: ZipFile, val root: String) extends InputStreamFileSystem {
|
|
||||||
private val directories = mutable.Map.empty[ZipEntry, Array[String]]
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
|
||||||
|
|
||||||
def spaceTotal = spaceUsed
|
def spaceTotal = spaceUsed
|
||||||
|
|
||||||
def spaceUsed = spaceUsed_
|
def spaceUsed = spaceUsed_
|
||||||
|
|
||||||
private lazy val spaceUsed_ = {
|
private lazy val spaceUsed_ = {
|
||||||
var size = 0L
|
def recurse(d: ArchiveDirectory): Long = d.children.foldLeft(0L)((acc, c) => acc + (c match {
|
||||||
val enum = zip.entries()
|
case directory: ArchiveDirectory => recurse(directory)
|
||||||
while (enum.hasMoreElements) size += enum.nextElement.getSize
|
case file: ArchiveFile => file.size
|
||||||
size
|
}))
|
||||||
|
recurse(archive)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
@ -26,48 +30,144 @@ class ZipFileInputStreamFileSystem(val zip: ZipFile, val root: String) extends I
|
|||||||
override def exists(path: String) = entry(path).isDefined
|
override def exists(path: String) = entry(path).isDefined
|
||||||
|
|
||||||
override def size(path: String) = entry(path) match {
|
override def size(path: String) = entry(path) match {
|
||||||
case Some(file) if !file.isDirectory => file.getSize
|
case Some(file) if !file.isDirectory => file.size
|
||||||
case _ => 0L
|
case _ => 0L
|
||||||
}
|
}
|
||||||
|
|
||||||
override def isDirectory(path: String) = entry(path).exists(_.isDirectory)
|
override def isDirectory(path: String) = entry(path).exists(_.isDirectory)
|
||||||
|
|
||||||
def lastModified(path: String) = entry(path) match {
|
def lastModified(path: String) = entry(path) match {
|
||||||
case Some(file) => file.getTime
|
case Some(file) => file.lastModified
|
||||||
case _ => 0L
|
case _ => 0L
|
||||||
}
|
}
|
||||||
|
|
||||||
override def list(path: String) = entry(path) match {
|
override def list(path: String) = entry(path) match {
|
||||||
case Some(entry) if entry.isDirectory => entries(entry)
|
case Some(entry) if entry.isDirectory => entry.list()
|
||||||
case _ => null
|
case _ => null
|
||||||
}
|
}
|
||||||
|
|
||||||
override def close() {
|
// ----------------------------------------------------------------------- //
|
||||||
super.close()
|
|
||||||
zip.close()
|
override protected def openInputStream(path: String) = entry(path).map(_.openStream())
|
||||||
directories.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
override protected def openInputStream(path: String) = entry(path).map(entry => zip.getInputStream(entry))
|
private def entry(path: String) = {
|
||||||
|
val cleanPath = "/" + path.replace("\\", "/").replace("//", "/").stripPrefix("/").stripSuffix("/")
|
||||||
// ----------------------------------------------------------------------- //
|
if (cleanPath == "/") Some(archive)
|
||||||
|
else archive.find(cleanPath.split("/"))
|
||||||
private def entry(path: String) = Option(zip.getEntry((root + path.replace("\\", "/")).replace("//", "/")))
|
|
||||||
|
|
||||||
private def entries(path: ZipEntry) = directories.get(path).getOrElse {
|
|
||||||
val pathName = root + path.getName
|
|
||||||
val list = mutable.ArrayBuffer.empty[String]
|
|
||||||
val iterator = zip.entries
|
|
||||||
while (iterator.hasMoreElements) {
|
|
||||||
val child = iterator.nextElement
|
|
||||||
if (child.getName.startsWith(pathName)) {
|
|
||||||
list += child.getName.substring(pathName.length)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val children = list.toArray
|
|
||||||
directories += path -> children
|
|
||||||
children
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object ZipFileInputStreamFileSystem {
|
||||||
|
private val cache = com.google.common.cache.CacheBuilder.newBuilder().
|
||||||
|
weakValues().
|
||||||
|
asInstanceOf[CacheBuilder[String, ArchiveDirectory]].
|
||||||
|
build[String, ArchiveDirectory]()
|
||||||
|
|
||||||
|
def fromFile(file: io.File, innerPath: String) = this.synchronized {
|
||||||
|
Option(cache.get(file.getPath, new Callable[ArchiveDirectory] {
|
||||||
|
def call = try {
|
||||||
|
val zip = new ZipFile(file.getPath)
|
||||||
|
val cleanedPath = innerPath.stripPrefix("/").stripSuffix("/") + "/"
|
||||||
|
val rootEntry = zip.getEntry(cleanedPath)
|
||||||
|
val result = if (rootEntry != null && rootEntry.isDirectory) {
|
||||||
|
val directories = mutable.Set.empty[ArchiveDirectory]
|
||||||
|
val files = mutable.Set.empty[ArchiveFile]
|
||||||
|
val iterator = zip.entries()
|
||||||
|
while (iterator.hasMoreElements) {
|
||||||
|
val entry = iterator.nextElement()
|
||||||
|
if (entry.getName.startsWith(cleanedPath)) {
|
||||||
|
if (entry.isDirectory) directories += new ArchiveDirectory(entry, cleanedPath)
|
||||||
|
else files += new ArchiveFile(zip, entry, cleanedPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var root: ArchiveDirectory = null
|
||||||
|
for (entry <- directories ++ files) {
|
||||||
|
if (entry.path.length > 0) {
|
||||||
|
val parent = entry.path.substring(0, entry.path.lastIndexOf('/') max 0)
|
||||||
|
directories.find(d => d.path == parent) match {
|
||||||
|
case Some(directory) => directory.children += entry
|
||||||
|
case _ =>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert(entry.isInstanceOf[ArchiveDirectory])
|
||||||
|
root = entry.asInstanceOf[ArchiveDirectory]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root
|
||||||
|
}
|
||||||
|
else null
|
||||||
|
zip.close()
|
||||||
|
result
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
case e: Throwable =>
|
||||||
|
OpenComputers.log.log(Level.WARNING, "Failed creating ZIP file system.", e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
})) match {
|
||||||
|
case Some(archive) => new ZipFileInputStreamFileSystem(archive)
|
||||||
|
case _ => null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Archive(entry: ZipEntry, root: String) {
|
||||||
|
val path = entry.getName.stripPrefix(root).stripSuffix("/")
|
||||||
|
|
||||||
|
val name = path.substring(path.lastIndexOf('/') + 1)
|
||||||
|
|
||||||
|
val lastModified = entry.getTime
|
||||||
|
|
||||||
|
val isDirectory = entry.isDirectory
|
||||||
|
|
||||||
|
def size: Int
|
||||||
|
|
||||||
|
def list(): Array[String]
|
||||||
|
|
||||||
|
def openStream(): io.InputStream
|
||||||
|
|
||||||
|
def find(path: Iterable[String]): Option[Archive]
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ArchiveFile(zip: ZipFile, entry: ZipEntry, root: String) extends Archive(entry, root) {
|
||||||
|
val data = {
|
||||||
|
val in = zip.getInputStream(entry)
|
||||||
|
Iterator.continually(in.read).takeWhile(-1 !=).map(_.toByte).toArray
|
||||||
|
}
|
||||||
|
|
||||||
|
val size = data.length
|
||||||
|
|
||||||
|
def list() = null
|
||||||
|
|
||||||
|
def openStream() = new ByteArrayInputStream(data)
|
||||||
|
|
||||||
|
def find(path: Iterable[String]) =
|
||||||
|
if (path.size == 1 && path.head == name) Some(this)
|
||||||
|
else None
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ArchiveDirectory(entry: ZipEntry, root: String) extends Archive(entry, root) {
|
||||||
|
val children = mutable.Set.empty[Archive]
|
||||||
|
|
||||||
|
val size = 0
|
||||||
|
|
||||||
|
def list() = children.map(c => c.name + (if (c.isDirectory) "/" else "")).toArray
|
||||||
|
|
||||||
|
def openStream() = null
|
||||||
|
|
||||||
|
def find(path: Iterable[String]) =
|
||||||
|
if (path.head == name) {
|
||||||
|
if (path.size == 1) Some(this)
|
||||||
|
else {
|
||||||
|
val subPath = path.drop(1)
|
||||||
|
children.map(_.find(subPath)).collectFirst {
|
||||||
|
case Some(a) => a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else None
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user