Open-source multiplayer game server compatible with the RuneScape client https://www.openrs2.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
openrs2/cache/src/main/kotlin/org/openrs2/cache/Js5Index.kt

379 lines
12 KiB

package org.openrs2.cache
import io.netty.buffer.ByteBuf
import io.netty.buffer.ByteBufUtil
import org.openrs2.buffer.readUnsignedIntSmart
import org.openrs2.buffer.writeUnsignedIntSmart
import org.openrs2.crypto.Whirlpool
public class Js5Index(
public var protocol: Js5Protocol,
public var version: Int = 0,
public var hasNames: Boolean = false,
public var hasDigests: Boolean = false,
public var hasLengths: Boolean = false,
public var hasUncompressedChecksums: Boolean = false
) : MutableNamedEntryCollection<Js5Index.MutableGroup>(::MutableGroup) {
public interface Group<out T : File> : NamedEntryCollection<T>, NamedEntry {
public val version: Int
public val checksum: Int
public val uncompressedChecksum: Int
public val length: Int
public val uncompressedLength: Int
}
public class MutableGroup internal constructor(
parent: MutableNamedEntryCollection<MutableGroup>,
override val id: Int
) : MutableNamedEntryCollection<MutableFile>(::MutableFile), MutableNamedEntry, Group<MutableFile> {
private var parent: MutableNamedEntryCollection<MutableGroup>? = parent
public override var version: Int = 0
public override var checksum: Int = 0
public override var uncompressedChecksum: Int = 0
public override var length: Int = 0
public override var uncompressedLength: Int = 0
public override var nameHash: Int = -1
set(value) {
parent?.rename(id, field, value)
field = value
}
public var digest: ByteArray? = null
set(value) {
require(value == null || value.size == Whirlpool.DIGESTBYTES)
field = value
}
override fun remove() {
parent?.remove(this)
parent = null
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
if (!super.equals(other)) return false
other as MutableGroup
if (id != other.id) return false
if (version != other.version) return false
if (checksum != other.checksum) return false
if (uncompressedChecksum != other.uncompressedChecksum) return false
if (length != other.length) return false
if (uncompressedLength != other.uncompressedLength) return false
if (nameHash != other.nameHash) return false
if (digest != null) {
if (other.digest == null) return false
if (!digest.contentEquals(other.digest)) return false
} else if (other.digest != null) return false
return true
}
override fun hashCode(): Int {
var result = super.hashCode()
result = 31 * result + id
result = 31 * result + version
result = 31 * result + checksum
result = 31 * result + uncompressedChecksum
result = 31 * result + length
result = 31 * result + uncompressedLength
result = 31 * result + nameHash
result = 31 * result + (digest?.contentHashCode() ?: 0)
return result
}
override fun toString(): String {
val digest = digest
val hex = if (digest != null) {
ByteBufUtil.hexDump(digest)
} else {
"null"
}
return "Group{id=$id, nameHash=$nameHash, version=$version, checksum=$checksum, " +
"uncompressedChecksum=$uncompressedChecksum, length=$length, uncompressedLength=$uncompressedLength, " +
"digest=$hex, size=$size, capacity=$capacity}"
}
}
public interface File : NamedEntry
public class MutableFile internal constructor(
parent: MutableNamedEntryCollection<MutableFile>,
override val id: Int,
) : MutableNamedEntry, File {
private var parent: MutableNamedEntryCollection<MutableFile>? = parent
public override var nameHash: Int = -1
set(value) {
parent?.rename(id, field, value)
field = value
}
override fun remove() {
parent?.remove(this)
parent = null
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as MutableFile
if (id != other.id) return false
if (nameHash != other.nameHash) return false
return true
}
override fun hashCode(): Int {
var result = id
result = 31 * result + nameHash
return result
}
override fun toString(): String {
return "File{id=$id, nameHash=$nameHash}"
}
}
public fun write(buf: ByteBuf) {
val writeFunc = if (protocol >= Js5Protocol.SMART) {
buf::writeUnsignedIntSmart
} else {
{ value ->
check(value in 0..65535) {
"$value outside of valid non-SMART range"
}
buf.writeShort(value)
}
}
buf.writeByte(protocol.id)
if (protocol >= Js5Protocol.VERSIONED) {
buf.writeInt(version)
}
var flags = 0
if (hasNames) {
flags = flags or FLAG_NAMES
}
if (hasDigests) {
flags = flags or FLAG_DIGESTS
}
if (hasLengths) {
flags = flags or FLAG_LENGTHS
}
if (hasUncompressedChecksums) {
flags = flags or FLAG_UNCOMPRESSED_CHECKSUMS
}
buf.writeByte(flags)
writeFunc(size)
var prevGroupId = 0
for (group in this) {
writeFunc(group.id - prevGroupId)
prevGroupId = group.id
}
if (hasNames) {
for (group in this) {
buf.writeInt(group.nameHash)
}
}
for (group in this) {
buf.writeInt(group.checksum)
}
if (hasUncompressedChecksums) {
for (group in this) {
buf.writeInt(group.uncompressedChecksum)
}
}
if (hasDigests) {
for (group in this) {
val digest = group.digest
if (digest != null) {
buf.writeBytes(digest)
} else {
buf.writeZero(Whirlpool.DIGESTBYTES)
}
}
}
if (hasLengths) {
for (group in this) {
buf.writeInt(group.length)
buf.writeInt(group.uncompressedLength)
}
}
for (group in this) {
buf.writeInt(group.version)
}
for (group in this) {
writeFunc(group.size)
}
for (group in this) {
var prevFileId = 0
for (file in group) {
writeFunc(file.id - prevFileId)
prevFileId = file.id
}
}
if (hasNames) {
for (group in this) {
for (file in group) {
buf.writeInt(file.nameHash)
}
}
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
if (!super.equals(other)) return false
other as Js5Index
if (protocol != other.protocol) return false
if (version != other.version) return false
if (hasNames != other.hasNames) return false
if (hasDigests != other.hasDigests) return false
if (hasLengths != other.hasLengths) return false
if (hasUncompressedChecksums != other.hasUncompressedChecksums) return false
return true
}
override fun hashCode(): Int {
var result = super.hashCode()
result = 31 * result + protocol.hashCode()
result = 31 * result + version
result = 31 * result + hasNames.hashCode()
result = 31 * result + hasDigests.hashCode()
result = 31 * result + hasLengths.hashCode()
result = 31 * result + hasUncompressedChecksums.hashCode()
return result
}
override fun toString(): String {
return "Js5Index{protocol=$protocol, version=$version, hasNames=$hasNames, hasDigests=$hasDigests, " +
"hasLengths=$hasLengths, hasUncompressedChecksums=$hasUncompressedChecksums, size=$size, " +
"capacity=$capacity}"
}
public companion object {
private const val FLAG_NAMES = 0x01
private const val FLAG_DIGESTS = 0x02
private const val FLAG_LENGTHS = 0x04
private const val FLAG_UNCOMPRESSED_CHECKSUMS = 0x08
public fun read(buf: ByteBuf): Js5Index {
val number = buf.readUnsignedByte().toInt()
val protocol = Js5Protocol.fromId(number)
require(protocol != null) {
"Unsupported JS5 protocol number: $number"
}
val readFunc = if (protocol >= Js5Protocol.SMART) {
buf::readUnsignedIntSmart
} else {
buf::readUnsignedShort
}
val version = if (protocol >= Js5Protocol.VERSIONED) {
buf.readInt()
} else {
0
}
val flags = buf.readUnsignedByte().toInt()
val size = readFunc()
val index = Js5Index(
protocol,
version,
hasNames = flags and FLAG_NAMES != 0,
hasDigests = flags and FLAG_DIGESTS != 0,
hasLengths = flags and FLAG_LENGTHS != 0,
hasUncompressedChecksums = flags and FLAG_UNCOMPRESSED_CHECKSUMS != 0
)
var prevGroupId = 0
for (i in 0 until size) {
prevGroupId += readFunc()
index.createOrGet(prevGroupId)
}
if (index.hasNames) {
for (group in index) {
group.nameHash = buf.readInt()
}
}
for (group in index) {
group.checksum = buf.readInt()
}
if (index.hasUncompressedChecksums) {
for (group in index) {
group.uncompressedChecksum = buf.readInt()
}
}
if (index.hasDigests) {
for (group in index) {
val digest = ByteArray(Whirlpool.DIGESTBYTES)
buf.readBytes(digest)
group.digest = digest
}
}
if (index.hasLengths) {
for (group in index) {
group.length = buf.readInt()
group.uncompressedLength = buf.readInt()
}
}
for (group in index) {
group.version = buf.readInt()
}
val groupSizes = IntArray(size) {
readFunc()
}
for ((i, group) in index.withIndex()) {
val groupSize = groupSizes[i]
var prevFileId = 0
for (j in 0 until groupSize) {
prevFileId += readFunc()
group.createOrGet(prevFileId)
}
}
if (index.hasNames) {
for (group in index) {
for (file in group) {
file.nameHash = buf.readInt()
}
}
}
return index
}
}
}