mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2025-12-09 16:45:44 -07:00
111 lines
No EOL
3.7 KiB
Kotlin
111 lines
No EOL
3.7 KiB
Kotlin
package js5server
|
|
|
|
import com.displee.cache.CacheLibrary
|
|
import io.ktor.utils.io.*
|
|
import js5server.ext.readUMedium
|
|
import kotlin.math.min
|
|
|
|
class FileServer(
|
|
private val provider: DataProvider,
|
|
private val versionTable: ByteArray
|
|
) {
|
|
|
|
/**
|
|
* Fulfills a request by sending the requested files data to the requester
|
|
*/
|
|
suspend fun fulfill(read: ByteReadChannel, write: ByteWriteChannel, prefetch: Boolean) {
|
|
val value = read.readUMedium()
|
|
val index = value shr 16
|
|
val archive = value and 0xffff
|
|
val data = data(index, archive) ?: return println("Unable to fulfill request $index $archive $prefetch.")
|
|
if (index == 255 && archive == 255) {
|
|
serve255(write, data)
|
|
} else {
|
|
serve(write, index, archive, data, prefetch)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return data for an [index]'s [archive] file or [versionTable] when index and archive are both 255
|
|
*/
|
|
fun data(index: Int, archive: Int): ByteArray? {
|
|
if (index == 255 && archive == 255) {
|
|
return versionTable
|
|
}
|
|
return provider.data(index, archive)
|
|
}
|
|
|
|
/**
|
|
* Writes [source] [offset] [size] to [write] and starting at [headerSize] inserting a [SEPARATOR] every [split] bytes
|
|
*/
|
|
private suspend fun serve255(write: ByteWriteChannel, source: ByteArray) {
|
|
write.writeByte(255)
|
|
write.writeShort(255)
|
|
write.writeByte(0)
|
|
write.writeInt(source.size)
|
|
write.writeFully(source)
|
|
}
|
|
|
|
/**
|
|
* Writes response header followed by the contents of [data] to [write]
|
|
*/
|
|
suspend fun serve(write: ByteWriteChannel, index: Int, archive: Int, data: ByteArray, prefetch: Boolean) {
|
|
val compression = data[0].toInt()
|
|
val size = getInt(data[1], data[2], data[3], data[4]) + if (compression != 0) 8 else 4
|
|
write.writeByte(index)
|
|
write.writeShort(archive)
|
|
write.writeByte(if (prefetch) compression or 0x80 else compression)
|
|
serve(write, HEADER, data, OFFSET, size, SPLIT)
|
|
}
|
|
|
|
/**
|
|
* Writes [source] [offset] [size] to [write] and starting at [headerSize] inserting a [SEPARATOR] every [split] bytes
|
|
*/
|
|
suspend fun serve(write: ByteWriteChannel, headerSize: Int, source: ByteArray, offset: Int, size: Int, split: Int) {
|
|
var length = min(size, split - headerSize)
|
|
write.writeFully(source, offset, length)
|
|
var written = length
|
|
while (written < size) {
|
|
write.writeByte(SEPARATOR)
|
|
|
|
length = if (size - written < split) size - written else split - 1
|
|
write.writeFully(source, written + offset, length)
|
|
written += length
|
|
}
|
|
}
|
|
|
|
companion object {
|
|
|
|
private fun getInt(b1: Byte, b2: Byte, b3: Byte, b4: Byte) = b1.toInt() shl 24 or (b2.toInt() and 0xff shl 16) or (b3.toInt() and 0xff shl 8) or (b4.toInt() and 0xff)
|
|
|
|
private const val SEPARATOR = 255
|
|
private const val HEADER = 4
|
|
private const val SPLIT = 512
|
|
private const val OFFSET = 1
|
|
}
|
|
|
|
/**
|
|
* Length of archive with [name] in [index]
|
|
*/
|
|
fun file(cacheLibrary: CacheLibrary, index: Int, name: String): Int {
|
|
val idx = cacheLibrary.index(index)
|
|
val archive = idx.archiveId(name)
|
|
if (archive == -1) {
|
|
return 0
|
|
}
|
|
return (idx.readArchiveSector(archive)?.size ?: 2) - 2
|
|
}
|
|
|
|
/**
|
|
* Length of all archives in [index]
|
|
*/
|
|
fun archive(cache: CacheLibrary, index: Int): Int {
|
|
var total = 0
|
|
val idx = cache.index(index)
|
|
idx.archiveIds().forEach { archive ->
|
|
total += idx.readArchiveSector(archive)?.size ?: 0
|
|
}
|
|
total += cache.index255?.readArchiveSector(index)?.size ?: 0
|
|
return total
|
|
}
|
|
} |