267 lines
6.2 KiB
Kotlin
267 lines
6.2 KiB
Kotlin
package ru.dbotthepony.kstarbound.api
|
|
|
|
import com.google.common.collect.ImmutableMap
|
|
import com.google.gson.JsonElement
|
|
import com.google.gson.JsonParser
|
|
import ru.dbotthepony.kstarbound.Starbound
|
|
import ru.dbotthepony.kstarbound.io.StarboundPak
|
|
import ru.dbotthepony.kstarbound.stream
|
|
import java.io.*
|
|
import java.nio.ByteBuffer
|
|
import java.util.stream.Stream
|
|
|
|
fun interface ISBFileLocator {
|
|
fun locate(path: String): IStarboundFile
|
|
fun exists(path: String): Boolean = locate(path).exists
|
|
|
|
/**
|
|
* @throws IllegalStateException if file is a directory
|
|
* @throws FileNotFoundException if file does not exist
|
|
*/
|
|
fun read(path: String) = locate(path).read()
|
|
|
|
/**
|
|
* @throws IllegalStateException if file is a directory
|
|
* @throws FileNotFoundException if file does not exist
|
|
*/
|
|
fun readDirect(path: String) = locate(path).readDirect()
|
|
|
|
/**
|
|
* @throws IllegalStateException if file is a directory
|
|
* @throws FileNotFoundException if file does not exist
|
|
*/
|
|
fun open(path: String) = locate(path).open()
|
|
|
|
/**
|
|
* @throws IllegalStateException if file is a directory
|
|
* @throws FileNotFoundException if file does not exist
|
|
*/
|
|
fun reader(path: String) = locate(path).reader()
|
|
}
|
|
|
|
interface IStarboundFile : ISBFileLocator {
|
|
val exists: Boolean
|
|
val isDirectory: Boolean
|
|
|
|
/**
|
|
* null if root
|
|
*/
|
|
val parent: IStarboundFile?
|
|
val isFile: Boolean
|
|
|
|
/**
|
|
* null if not a directory
|
|
*/
|
|
val children: Map<String, IStarboundFile>?
|
|
val name: String
|
|
|
|
fun orNull(): IStarboundFile? = if (exists) this else null
|
|
|
|
operator fun get(name: String): IStarboundFile? {
|
|
return children?.get(name)
|
|
}
|
|
|
|
fun computeFullPath(): String {
|
|
var path = name
|
|
var parent = parent
|
|
|
|
while (parent != null) {
|
|
path = parent.name + "/" + path
|
|
parent = parent.parent
|
|
}
|
|
|
|
return path
|
|
}
|
|
|
|
fun computeDirectory(): String {
|
|
var path = ""
|
|
var parent = parent
|
|
|
|
while (parent != null) {
|
|
if (path == "")
|
|
path = parent.name
|
|
else
|
|
path = parent.name + "/" + path
|
|
|
|
parent = parent.parent
|
|
}
|
|
|
|
return path
|
|
}
|
|
|
|
override fun locate(path: String): IStarboundFile {
|
|
@Suppress("name_shadowing")
|
|
val path = path.trim()
|
|
|
|
if (path == "" || path == ".") {
|
|
return this
|
|
}
|
|
|
|
if (path == "..") {
|
|
return parent ?: NonExistingFile(computeFullPath() + "/..")
|
|
}
|
|
|
|
val split = path.lowercase().split("/")
|
|
var file = this
|
|
|
|
for (splitIndex in split.indices) {
|
|
if (split[splitIndex].isEmpty()) {
|
|
continue
|
|
}
|
|
|
|
val children = file.children ?: return NonExistingFile(name = split.last(), fullPath = computeFullPath() + "/" + path)
|
|
val find = children[split[splitIndex]]
|
|
|
|
if (find is StarboundPak.SBDirectory) {
|
|
file = find
|
|
} else if (find is StarboundPak.SBFile) {
|
|
if (splitIndex + 1 != split.size) {
|
|
return NonExistingFile(name = split.last(), fullPath = computeFullPath() + "/" + path)
|
|
}
|
|
|
|
return find
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
|
|
return NonExistingFile(name = split.last(), fullPath = computeFullPath() + "/" + path)
|
|
}
|
|
|
|
/**
|
|
* @throws IllegalStateException if file is a directory
|
|
* @throws FileNotFoundException if file does not exist
|
|
*/
|
|
fun open(): InputStream
|
|
|
|
/**
|
|
* @throws IllegalStateException if file is a directory
|
|
* @throws FileNotFoundException if file does not exist
|
|
*/
|
|
fun reader(): Reader = InputStreamReader(BufferedInputStream(open()))
|
|
|
|
/**
|
|
* @throws IllegalStateException if file is a directory
|
|
* @throws FileNotFoundException if file does not exist
|
|
*/
|
|
fun read(): ByteBuffer {
|
|
val stream = open()
|
|
val read = stream.readAllBytes()
|
|
stream.close()
|
|
return ByteBuffer.wrap(read)
|
|
}
|
|
|
|
/**
|
|
* @throws IllegalStateException if file is a directory
|
|
* @throws FileNotFoundException if file does not exist
|
|
*/
|
|
fun readToString(): String {
|
|
val stream = open()
|
|
val read = stream.readAllBytes()
|
|
stream.close()
|
|
return String(read, charset = Charsets.UTF_8)
|
|
}
|
|
|
|
/**
|
|
* @throws IllegalStateException if file is a directory
|
|
* @throws FileNotFoundException if file does not exist
|
|
*/
|
|
fun readDirect(): ByteBuffer {
|
|
val read = read()
|
|
val buf = ByteBuffer.allocateDirect(read.capacity())
|
|
|
|
read.position(0)
|
|
|
|
for (i in 0 until read.capacity()) {
|
|
buf.put(read[i])
|
|
}
|
|
|
|
buf.position(0)
|
|
|
|
return buf
|
|
}
|
|
|
|
/**
|
|
* non existent file, without any context
|
|
*/
|
|
companion object : IStarboundFile {
|
|
override val exists: Boolean
|
|
get() = false
|
|
override val isDirectory: Boolean
|
|
get() = false
|
|
override val parent: IStarboundFile?
|
|
get() = null
|
|
override val isFile: Boolean
|
|
get() = false
|
|
override val children: Map<String, IStarboundFile>?
|
|
get() = null
|
|
override val name: String
|
|
get() = ""
|
|
|
|
override fun open(): InputStream {
|
|
throw FileNotFoundException()
|
|
}
|
|
}
|
|
}
|
|
|
|
class NonExistingFile(
|
|
override val name: String,
|
|
override val parent: IStarboundFile? = null,
|
|
val fullPath: String? = null
|
|
) : IStarboundFile {
|
|
override val isDirectory: Boolean
|
|
get() = false
|
|
override val isFile: Boolean
|
|
get() = false
|
|
override val children: Map<String, IStarboundFile>?
|
|
get() = null
|
|
|
|
override val exists: Boolean
|
|
get() = false
|
|
|
|
override fun open(): InputStream {
|
|
throw FileNotFoundException("File ${fullPath ?: computeFullPath()} does not exist")
|
|
}
|
|
}
|
|
|
|
fun IStarboundFile.explore(): Stream<IStarboundFile> {
|
|
val children = children ?: return Stream.of(this)
|
|
return Stream.concat(Stream.of(this), children.values.stream().flatMap { it.explore() })
|
|
}
|
|
|
|
fun getPathFolder(path: String): String {
|
|
return path.substringBeforeLast('/')
|
|
}
|
|
|
|
fun getPathFilename(path: String): String {
|
|
return path.substringAfterLast('/')
|
|
}
|
|
|
|
class PhysicalFile(val real: File) : IStarboundFile {
|
|
override val exists: Boolean
|
|
get() = real.exists()
|
|
override val isDirectory: Boolean
|
|
get() = real.isDirectory
|
|
override val parent: PhysicalFile?
|
|
get() {
|
|
return PhysicalFile(real.parentFile ?: return null)
|
|
}
|
|
|
|
override val isFile: Boolean
|
|
get() = real.isFile
|
|
override val children: Map<String, PhysicalFile>?
|
|
get() {
|
|
return real.list()?.stream()?.map { it to PhysicalFile(File(it)) }?.collect(ImmutableMap.toImmutableMap({ it.first }, { it.second }))
|
|
}
|
|
override val name: String
|
|
get() = real.name
|
|
|
|
override fun open(): InputStream {
|
|
return BufferedInputStream(real.inputStream())
|
|
}
|
|
|
|
override fun toString(): String {
|
|
return "PhysicalFile[$real]"
|
|
}
|
|
}
|