diff --git a/LICENSE b/LICENSE index 0b30a5d..79a8131 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2021 Xlite - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2021 Xlite + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 83b5de7..cfd0317 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,9 @@ # RuneTopic Cache Library [![Discord](https://img.shields.io/discord/212385463418355713?color=%237289DA&logo=Discord&logoColor=%237289DA)](https://discord.gg/3scgBkrfMG) -[![License](https://img.shields.io/github/license/xlite2/xlite)](#) A cache library written in Kotlin. -# Requirements -- Java Version 16 - # Supported - RS2 (414-772) - RS3 (773-~788) @@ -27,15 +23,15 @@ A cache library written in Kotlin. # Implementation Just use cache if you do not require any of the revision specific loaders. -``` +```groovy cache = { module = "com.runetopic.cache:cache", version.ref "1.4.19-SNAPSHOT" } loader = { module = "com.runetopic.cache:loader", version.ref "647.6.4-SNAPSHOT" } ``` -``` +```groovy //SNAPSHOTS maven { - url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/") + url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/") } ``` @@ -43,75 +39,91 @@ maven { Index -> Group -> File ### Creating a new JS5 store -``` +```kotlin val store = Js5Store(path = Path.of("/path/"), parallel = true) ``` ### Getting an index -``` +```kotlin val index = store.index(indexId = 5) ``` ### Getting a group by group id -``` +```kotlin val index = store.index(indexId = 5) val group = index.group(groupId = 360) ``` ### Getting a group by group name -``` +```kotlin val index = store.index(indexId = 5) val group = index.group(groupName = "m50_50") ``` ### Getting a file from a group by id -``` +```kotlin val index = store.index(indexId = 2) val group = index.group(groupId = 26) val file = group.file(fileId = 1000) ``` ### Looping multiple groups from an index - store.index(indexId = 19).use { index -> - (0 until index.expand()).forEach { - val data = index.group(it ushr 8).file(it and 0xFF).data - } +```kotlin +store.index(indexId = 19).use { index -> + (0 until index.expand()).forEach { + val data = index.group(it ushr 8).file(it and 0xFF).data } +} +``` ### Looping multiple files from a group - store.index(indexId = 2).group(groupId = 26).files().forEach { - val id = it.id - val data = it.data - } - +```kotlin +store.index(indexId = 2).group(groupId = 26).files().forEach { + val id = it.id + val data = it.data +} +``` ### Getting the reference table of an index and group by id. -```store.groupReferenceTable(indexId = 255, groupId = 255)``` +```kotlin +val size = store.groupReferenceTable(indexId = 255, groupId = 255) +``` ### Getting an index reference table size by id -```store.indexReferenceTableSize(indexId = 28)``` +```kotlin +val size = store.indexReferenceTableSize(indexId = 28) +``` ### Getting a group reference table size by name -```store.groupReferenceTableSize(indexId = 30, groupName = "windows/x86/jaclib.dll")``` +```kotlin +val size = store.groupReferenceTableSize(indexId = 30, groupName = "windows/x86/jaclib.dll") +``` ### Getting a group reference table size by id -```store.groupReferenceTableSize(indexId = 30, groupId = 6)``` +```kotlin +val size = store.groupReferenceTableSize(indexId = 30, groupId = 6) +``` ### Getting 255, 255 checksums with RSA/Whirlpool -```val checksums = store.checksumsWithRSA(exponent = BigInteger(""), modulus = BigInteger(""))``` +```kotlin +val checksums = store.checksumsWithRSA(exponent = BigInteger(""), modulus = BigInteger("")) +``` ### Getting 255, 255 checksums without RSA/Whirlpool -```val checksums = store.checksumsWithoutRSA()``` +```kotlin +val checksums = store.checksumsWithoutRSA() +``` ### An example of a single thread loading providers -``` -objs().load(store) -npcs().load(store) -locs().load(store) -particles().load(store) + +```kotlin +val objProvider = objs().load(store) +val npcProvider = npcs().load(store) +val locProvider = locs().load(store) +val particleProvider = particles().load(store) ``` ### An example of multiple threads parallel loading providers -``` +```kotlin val pool = Executors.newFixedThreadPool(4) val providers = listOf( objs(), diff --git a/build.gradle.kts b/build.gradle.kts index 0e8e4f4..0474a54 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,15 +8,17 @@ configure(allprojects) { group = "com.runetopic.cache" plugins.withType { - java.sourceCompatibility = JavaVersion.VERSION_16 - java.targetCompatibility = JavaVersion.VERSION_16 + java.sourceCompatibility = JavaVersion.VERSION_17 + java.targetCompatibility = JavaVersion.VERSION_17 tasks { compileKotlin { kotlinOptions.jvmTarget = "1.8" + kotlinOptions.freeCompilerArgs = listOf("-Xopt-in=kotlin.RequiresOptIn") } compileTestKotlin { kotlinOptions.jvmTarget = "1.8" + kotlinOptions.freeCompilerArgs = listOf("-Xopt-in=kotlin.RequiresOptIn") } } } diff --git a/cache/.gitignore b/cache/.gitignore new file mode 100644 index 0000000..ef7cf87 --- /dev/null +++ b/cache/.gitignore @@ -0,0 +1,3 @@ +build/ +src/test/resources/main_file_cache.dat2 +src/test/resources/main_file_cache.idx10 \ No newline at end of file diff --git a/cache/build.gradle.kts b/cache/build.gradle.kts index 69688bb..0d07adc 100644 --- a/cache/build.gradle.kts +++ b/cache/build.gradle.kts @@ -4,7 +4,7 @@ plugins { signing } -version = "1.4.19-SNAPSHOT" +version = "1.4.20-SNAPSHOT" java { withJavadocJar() @@ -67,7 +67,6 @@ publishing { } } - signing { sign(publishing.publications["mavenJava"]) } @@ -78,4 +77,7 @@ dependencies { implementation("org.slf4j:slf4j-simple:1.7.32") implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.+") implementation("com.runetopic.cryptography:cryptography:1.0.6-SNAPSHOT") + + testImplementation("org.jetbrains.kotlin:kotlin-test") + testImplementation("io.mockk:mockk:1.12.0") } diff --git a/cache/src/main/kotlin/com/runetopic/cache/codec/CodecType.kt b/cache/src/main/kotlin/com/runetopic/cache/codec/CodecType.kt index f7642a3..64a8611 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/codec/CodecType.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/codec/CodecType.kt @@ -11,10 +11,10 @@ import com.runetopic.cache.codec.impl.NoFileCodec * @author Jordan Abraham */ internal sealed class CodecType( - val codec: IFileCodec + val codec: FileCodec ) { - object BadCodec: CodecType(NoFileCodec()) - object NoCodec: CodecType(NoFileCodec()) - object BZipCodec: CodecType(BZip2Codec()) - object GZipCodec: CodecType(GZipCodec()) -} \ No newline at end of file + object BadCodec : CodecType(NoFileCodec()) + object NoCodec : CodecType(NoFileCodec()) + object BZipCodec : CodecType(BZip2Codec()) + object GZipCodec : CodecType(GZipCodec()) +} diff --git a/cache/src/main/kotlin/com/runetopic/cache/codec/ContainerCodec.kt b/cache/src/main/kotlin/com/runetopic/cache/codec/ContainerCodec.kt index 7a3d39a..6c6b8ad 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/codec/ContainerCodec.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/codec/ContainerCodec.kt @@ -1,12 +1,17 @@ package com.runetopic.cache.codec -import com.runetopic.cache.codec.CodecType.* +import com.runetopic.cache.codec.CodecType.BZipCodec +import com.runetopic.cache.codec.CodecType.BadCodec +import com.runetopic.cache.codec.CodecType.GZipCodec +import com.runetopic.cache.codec.CodecType.NoCodec import com.runetopic.cache.exception.CompressionException import com.runetopic.cache.extension.readUnsignedByte import com.runetopic.cache.extension.readUnsignedShort import com.runetopic.cache.extension.remainingBytes import com.runetopic.cache.extension.toByteBuffer import com.runetopic.cryptography.fromXTEA +import java.lang.Exception +import java.nio.ByteBuffer import java.util.zip.CRC32 /** @@ -17,7 +22,34 @@ import java.util.zip.CRC32 */ internal object ContainerCodec { - fun decompress(data: ByteArray, keys: IntArray = intArrayOf()): Container { + fun compress( + compression: Int, + revision: Int, + data: ByteArray, + keys: IntArray = intArrayOf() + ): ByteBuffer { + val codec = compressionCodec(compression) + val compressed = if (codec != NoCodec) codec.codec.compress(data, keys) else data + val buffer = ByteBuffer.allocate((if (codec != NoCodec) 9 else 5) + compressed.size/* + if (revision == -1) 0 else 2*/) + buffer.put(compressionType(codec).toByte()) + buffer.putInt(compressed.size) + if (codec != NoCodec) { + buffer.putInt(data.size) + } + buffer.put(compressed) + if (keys.isNotEmpty()) { + // TODO xtea + } + if (revision != -1) { + // buffer.putShort(revision.toShort()) + } + return buffer + } + + fun decompress( + data: ByteArray, + keys: IntArray = intArrayOf() + ): Container { val buffer = data.toByteBuffer() val compression = buffer.readUnsignedByte() val length = buffer.int @@ -29,7 +61,7 @@ internal object ContainerCodec { val crc32 = CRC32() crc32.update(data, 0, 5) - return when (val type = compressionType(compression)) { + return when (val type = compressionCodec(compression)) { BadCodec -> throw CompressionException("Compression type not found with a compression opcode of $compression.") is NoCodec -> { val encrypted = ByteArray(length) @@ -41,7 +73,7 @@ internal object ContainerCodec { Container(decrypted, compression, revision, crc32.value.toInt()) } - GZipCodec, BZipCodec-> { + GZipCodec, BZipCodec -> { val encrypted = ByteArray(length + 4) buffer.get(encrypted) crc32.update(encrypted, 0, encrypted.size) @@ -66,7 +98,16 @@ internal object ContainerCodec { } } - private fun compressionType(compression: Int): CodecType { + private fun compressionType(codecType: CodecType): Int { + return when (codecType) { + is NoCodec -> 0 + is BZipCodec -> 1 + is GZipCodec -> 2 + else -> throw Exception("Bad compression type.") + } + } + + private fun compressionCodec(compression: Int): CodecType { return when (compression) { 0 -> NoCodec 1 -> BZipCodec @@ -74,4 +115,4 @@ internal object ContainerCodec { else -> BadCodec } } -} \ No newline at end of file +} diff --git a/cache/src/main/kotlin/com/runetopic/cache/codec/Decompression.kt b/cache/src/main/kotlin/com/runetopic/cache/codec/Decompression.kt index 670f9bc..11930af 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/codec/Decompression.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/codec/Decompression.kt @@ -5,4 +5,4 @@ package com.runetopic.cache.codec * @author Jordan Abraham */ fun ByteArray.decompress(keys: IntArray): ByteArray = ContainerCodec.decompress(this, keys).data -fun ByteArray.decompress(): ByteArray = ContainerCodec.decompress(this, intArrayOf()).data \ No newline at end of file +fun ByteArray.decompress(): ByteArray = ContainerCodec.decompress(this, intArrayOf()).data diff --git a/cache/src/main/kotlin/com/runetopic/cache/codec/IFileCodec.kt b/cache/src/main/kotlin/com/runetopic/cache/codec/FileCodec.kt similarity index 61% rename from cache/src/main/kotlin/com/runetopic/cache/codec/IFileCodec.kt rename to cache/src/main/kotlin/com/runetopic/cache/codec/FileCodec.kt index 0870f5f..aac1c44 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/codec/IFileCodec.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/codec/FileCodec.kt @@ -4,7 +4,7 @@ package com.runetopic.cache.codec * @author Tyler Telis * @email */ -internal interface IFileCodec { - fun compress(data: ByteArray, length: Int, keys: IntArray): ByteArray +internal interface FileCodec { + fun compress(data: ByteArray, keys: IntArray): ByteArray fun decompress(data: ByteArray, length: Int, keys: IntArray): ByteArray -} \ No newline at end of file +} diff --git a/cache/src/main/kotlin/com/runetopic/cache/codec/impl/BZip2Codec.kt b/cache/src/main/kotlin/com/runetopic/cache/codec/impl/BZip2Codec.kt index 9fb333f..b8f795d 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/codec/impl/BZip2Codec.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/codec/impl/BZip2Codec.kt @@ -1,6 +1,6 @@ package com.runetopic.cache.codec.impl -import com.runetopic.cache.codec.IFileCodec +import com.runetopic.cache.codec.FileCodec import com.runetopic.cache.store.Constants.BZIP_HEADER import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream @@ -14,8 +14,8 @@ import java.util.* * @author Tyler Telis * @email */ -internal class BZip2Codec: IFileCodec { - override fun compress(data: ByteArray, length: Int, keys: IntArray): ByteArray { +internal class BZip2Codec : FileCodec { + override fun compress(data: ByteArray, keys: IntArray): ByteArray { val stream: InputStream = ByteArrayInputStream(data) val bout = ByteArrayOutputStream() BZip2CompressorOutputStream(bout, 1).use { os -> IOUtils.copy(stream, os) } @@ -33,11 +33,11 @@ internal class BZip2Codec: IFileCodec { override fun decompress(data: ByteArray, length: Int, keys: IntArray): ByteArray { val buffer = ByteArray(length + BZIP_HEADER.size) - System.arraycopy(BZIP_HEADER, 0, buffer,0, BZIP_HEADER.size) + System.arraycopy(BZIP_HEADER, 0, buffer, 0, BZIP_HEADER.size) System.arraycopy(data, 0, buffer, BZIP_HEADER.size, length) val stream = ByteArrayOutputStream() BZip2CompressorInputStream(ByteArrayInputStream(buffer)).use { IOUtils.copy(it, stream) } return stream.toByteArray() } -} \ No newline at end of file +} diff --git a/cache/src/main/kotlin/com/runetopic/cache/codec/impl/GZipCodec.kt b/cache/src/main/kotlin/com/runetopic/cache/codec/impl/GZipCodec.kt index c5d11f6..2bd4c0d 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/codec/impl/GZipCodec.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/codec/impl/GZipCodec.kt @@ -1,6 +1,6 @@ package com.runetopic.cache.codec.impl -import com.runetopic.cache.codec.IFileCodec +import com.runetopic.cache.codec.FileCodec import org.apache.commons.compress.utils.IOUtils import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream @@ -11,8 +11,8 @@ import java.util.zip.GZIPOutputStream * @author Tyler Telis * @email */ -internal class GZipCodec: IFileCodec { - override fun compress(data: ByteArray, length: Int, keys: IntArray): ByteArray { +internal class GZipCodec : FileCodec { + override fun compress(data: ByteArray, keys: IntArray): ByteArray { val inputStream = ByteArrayInputStream(data) val outputStream = ByteArrayOutputStream() GZIPOutputStream(outputStream).use { os -> IOUtils.copy(inputStream, os) } @@ -26,4 +26,4 @@ internal class GZipCodec: IFileCodec { } return outputStream.toByteArray() } -} \ No newline at end of file +} diff --git a/cache/src/main/kotlin/com/runetopic/cache/codec/impl/NoFileCodec.kt b/cache/src/main/kotlin/com/runetopic/cache/codec/impl/NoFileCodec.kt index 9ab7f5f..586926a 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/codec/impl/NoFileCodec.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/codec/impl/NoFileCodec.kt @@ -1,12 +1,12 @@ package com.runetopic.cache.codec.impl -import com.runetopic.cache.codec.IFileCodec +import com.runetopic.cache.codec.FileCodec /** * @author Tyler Telis * @email */ -internal class NoFileCodec: IFileCodec { - override fun compress(data: ByteArray, length: Int, keys: IntArray): ByteArray = throw NotImplementedError("No codec provided.") +internal class NoFileCodec : FileCodec { + override fun compress(data: ByteArray, keys: IntArray): ByteArray = throw NotImplementedError("No codec provided.") override fun decompress(data: ByteArray, length: Int, keys: IntArray): ByteArray = throw NotImplementedError("No codec provided.") -} \ No newline at end of file +} diff --git a/cache/src/main/kotlin/com/runetopic/cache/exception/CompressionException.kt b/cache/src/main/kotlin/com/runetopic/cache/exception/CompressionException.kt index 82cbed5..039220a 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/exception/CompressionException.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/exception/CompressionException.kt @@ -4,4 +4,4 @@ package com.runetopic.cache.exception * @author Tyler Telis * @email */ -internal class CompressionException(override val message: String): RuntimeException(message) \ No newline at end of file +internal class CompressionException(override val message: String) : RuntimeException(message) diff --git a/cache/src/main/kotlin/com/runetopic/cache/exception/DatFileException.kt b/cache/src/main/kotlin/com/runetopic/cache/exception/DatFileException.kt index b5e84e5..42b5153 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/exception/DatFileException.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/exception/DatFileException.kt @@ -4,4 +4,4 @@ package com.runetopic.cache.exception * @author Tyler Telis * @email */ -internal class DatFileException(override val message: String): RuntimeException(message) \ No newline at end of file +internal class DatFileException(override val message: String) : RuntimeException(message) diff --git a/cache/src/main/kotlin/com/runetopic/cache/exception/EndOfDatFileException.kt b/cache/src/main/kotlin/com/runetopic/cache/exception/EndOfDatFileException.kt index 709969c..4a77602 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/exception/EndOfDatFileException.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/exception/EndOfDatFileException.kt @@ -4,4 +4,4 @@ package com.runetopic.cache.exception * @author Tyler Telis * @email */ -internal class EndOfDatFileException(override val message: String): RuntimeException(message) \ No newline at end of file +internal class EndOfDatFileException(override val message: String) : RuntimeException(message) diff --git a/cache/src/main/kotlin/com/runetopic/cache/exception/FileDataException.kt b/cache/src/main/kotlin/com/runetopic/cache/exception/FileDataException.kt index 2d646ce..62b487f 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/exception/FileDataException.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/exception/FileDataException.kt @@ -4,4 +4,4 @@ package com.runetopic.cache.exception * @author Tyler Telis * @email */ -internal class FileDataException(override val message: String): RuntimeException(message) \ No newline at end of file +internal class FileDataException(override val message: String) : RuntimeException(message) diff --git a/cache/src/main/kotlin/com/runetopic/cache/exception/FileNotFoundException.kt b/cache/src/main/kotlin/com/runetopic/cache/exception/FileNotFoundException.kt index fa7aebd..d4786f6 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/exception/FileNotFoundException.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/exception/FileNotFoundException.kt @@ -4,4 +4,4 @@ package com.runetopic.cache.exception * @author Tyler Telis * @email */ -internal class FileNotFoundException(override val message: String): RuntimeException(message) \ No newline at end of file +internal class FileNotFoundException(override val message: String) : RuntimeException(message) diff --git a/cache/src/main/kotlin/com/runetopic/cache/exception/IdxFileException.kt b/cache/src/main/kotlin/com/runetopic/cache/exception/IdxFileException.kt index 70e2bd7..2faba92 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/exception/IdxFileException.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/exception/IdxFileException.kt @@ -4,4 +4,4 @@ package com.runetopic.cache.exception * @author Tyler Telis * @email */ -internal class IdxFileException(override val message: String): RuntimeException(message) \ No newline at end of file +internal class IdxFileException(override val message: String) : RuntimeException(message) diff --git a/cache/src/main/kotlin/com/runetopic/cache/exception/ProtocolException.kt b/cache/src/main/kotlin/com/runetopic/cache/exception/ProtocolException.kt index 97b9d4e..cfdcb3d 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/exception/ProtocolException.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/exception/ProtocolException.kt @@ -4,4 +4,4 @@ package com.runetopic.cache.exception * @author Tyler Telis * @email */ -internal class ProtocolException(override val message: String): RuntimeException(message) \ No newline at end of file +internal class ProtocolException(override val message: String) : RuntimeException(message) diff --git a/cache/src/main/kotlin/com/runetopic/cache/extension/ByteArray.kt b/cache/src/main/kotlin/com/runetopic/cache/extension/ByteArray.kt index 5e1fc85..ee2b93b 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/extension/ByteArray.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/extension/ByteArray.kt @@ -5,4 +5,4 @@ import java.nio.ByteBuffer /** * @author Jordan Abraham */ -fun ByteArray.toByteBuffer(): ByteBuffer = ByteBuffer.wrap(this) \ No newline at end of file +fun ByteArray.toByteBuffer(): ByteBuffer = ByteBuffer.wrap(this) diff --git a/cache/src/main/kotlin/com/runetopic/cache/extension/ByteBuffer.kt b/cache/src/main/kotlin/com/runetopic/cache/extension/ByteBuffer.kt index bcf0e3c..76e94cb 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/extension/ByteBuffer.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/extension/ByteBuffer.kt @@ -6,9 +6,16 @@ internal fun ByteBuffer.readUnsignedByte(): Int = get().toInt() and 0xFF internal fun ByteBuffer.readUnsignedShort(): Int = short.toInt() and 0xFFFF internal fun ByteBuffer.readUnsignedMedium(): Int = ((readUnsignedByte() shl 16) or (readUnsignedByte() shl 8) or readUnsignedByte()) internal fun ByteBuffer.readUnsignedIntShortSmart(): Int = if (get(position()).toInt() < 0) int and Int.MAX_VALUE else readUnsignedShort() +internal fun ByteBuffer.putIntShortSmart(value: Int) { + val buffer = when { + value >= Short.MAX_VALUE -> ByteBuffer.allocate(Int.SIZE_BYTES).putInt(value - Int.MAX_VALUE - 1) + else -> ByteBuffer.allocate(Short.SIZE_BYTES).putShort(if (value >= 0) value.toShort() else Short.MAX_VALUE) + } + get(buffer.array()) +} internal fun ByteBuffer.remainingBytes(): ByteArray { val bytes = ByteArray(remaining()) get(bytes) return bytes -} \ No newline at end of file +} diff --git a/cache/src/main/kotlin/com/runetopic/cache/extension/String.kt b/cache/src/main/kotlin/com/runetopic/cache/extension/String.kt index 13f876f..98c196d 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/extension/String.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/extension/String.kt @@ -4,4 +4,4 @@ internal fun String.nameHash(): Int { var hash = 0 this.forEach { element -> hash = element.toInt() + ((hash shl 5) - hash) } return hash -} \ No newline at end of file +} diff --git a/cache/src/main/kotlin/com/runetopic/cache/hierarchy/ReferenceTable.kt b/cache/src/main/kotlin/com/runetopic/cache/hierarchy/ReferenceTable.kt index 7f9dd6d..4142b42 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/hierarchy/ReferenceTable.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/hierarchy/ReferenceTable.kt @@ -32,4 +32,4 @@ internal data class ReferenceTable( result = 31 * result + length return result } -} \ No newline at end of file +} diff --git a/cache/src/main/kotlin/com/runetopic/cache/hierarchy/index/Index.kt b/cache/src/main/kotlin/com/runetopic/cache/hierarchy/index/Index.kt index b803e0e..c966886 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/hierarchy/index/Index.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/hierarchy/index/Index.kt @@ -17,12 +17,16 @@ data class Index( val protocol: Int, val revision: Int, val isNamed: Boolean, + val isUsingWhirlpool: Boolean, private val groups: Map -): Comparable { +) : Comparable { @JvmName("getGroups") fun groups(): Collection = groups.values + @JvmName("getGroupIds") + fun groupIds(): Collection = groups.keys + @JvmName("getGroup") fun group(groupId: Int): Group = groups[groupId] ?: Group.DEFAULT @@ -30,7 +34,6 @@ data class Index( fun group(groupName: String): Group = groups.values.find { it.nameHash == groupName.nameHash() } ?: Group.DEFAULT fun expand(): Int = groups.values.last().files().size + (groups.values.last().id shl 8) - fun use(block: (Index) -> Unit) = block.invoke(this) override fun compareTo(other: Index): Int = this.id.compareTo(other.id) override fun equals(other: Any?): Boolean { @@ -64,6 +67,6 @@ data class Index( } internal companion object { - fun default(indexId: Int): Index = Index(indexId, 0, ByteArray(64), -1, -1, 0, false, hashMapOf()) + fun default(indexId: Int): Index = Index(indexId, 0, ByteArray(64), -1, -1, 0, false, false, hashMapOf()) } -} \ No newline at end of file +} diff --git a/cache/src/main/kotlin/com/runetopic/cache/hierarchy/index/group/Group.kt b/cache/src/main/kotlin/com/runetopic/cache/hierarchy/index/group/Group.kt index 565b50c..bd7c77a 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/hierarchy/index/group/Group.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/hierarchy/index/group/Group.kt @@ -15,16 +15,23 @@ data class Group( val whirlpool: ByteArray, val revision: Int, val keys: IntArray, - private val files: Map, + private val files: MutableMap, val data: ByteArray -): Comparable { +) : Comparable { @JvmName("getFiles") fun files(): Collection = files.values + @JvmName("getFileIds") + fun fileIds(): Collection = files.keys + @JvmName("getFile") fun file(fileId: Int): File = files[fileId] ?: File.DEFAULT + internal fun putFile(id: Int, file: File) { + files[id] = file + } + override fun compareTo(other: Group): Int = this.id.compareTo(other.id) override fun equals(other: Any?): Boolean { if (this === other) return true @@ -57,6 +64,6 @@ data class Group( } internal companion object { - val DEFAULT = Group(-1, -1, -1, byteArrayOf(), -1, intArrayOf(), mapOf(), byteArrayOf()) + val DEFAULT = Group(-1, -1, 0, byteArrayOf(), 0, intArrayOf(), mutableMapOf(), byteArrayOf()) } } diff --git a/cache/src/main/kotlin/com/runetopic/cache/hierarchy/index/group/file/File.kt b/cache/src/main/kotlin/com/runetopic/cache/hierarchy/index/group/file/File.kt index 652c5af..75cf3df 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/hierarchy/index/group/file/File.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/hierarchy/index/group/file/File.kt @@ -10,7 +10,7 @@ data class File( val id: Int, val nameHash: Int, val data: ByteArray -): Comparable { +) : Comparable { override fun compareTo(other: File): Int = this.id.compareTo(other.id) override fun equals(other: Any?): Boolean { @@ -34,6 +34,6 @@ data class File( } internal companion object { - val DEFAULT = File(-1, -1, byteArrayOf(0)) + val DEFAULT = File(-1, 0, byteArrayOf(0)) } -} \ No newline at end of file +} diff --git a/cache/src/main/kotlin/com/runetopic/cache/store/Constants.kt b/cache/src/main/kotlin/com/runetopic/cache/store/Constants.kt index ce769b7..43b275b 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/store/Constants.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/store/Constants.kt @@ -20,4 +20,4 @@ internal object Constants { 'h'.code.toByte(), '1'.code.toByte() ) -} \ No newline at end of file +} diff --git a/cache/src/main/kotlin/com/runetopic/cache/store/Js5Store.kt b/cache/src/main/kotlin/com/runetopic/cache/store/Js5Store.kt index 4da3a79..fa629d4 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/store/Js5Store.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/store/Js5Store.kt @@ -1,6 +1,7 @@ package com.runetopic.cache.store import com.runetopic.cache.hierarchy.index.Index +import com.runetopic.cache.hierarchy.index.group.file.File import com.runetopic.cache.store.storage.js5.Js5DiskStorage import com.runetopic.cryptography.toWhirlpool import java.io.Closeable @@ -23,7 +24,7 @@ class Js5Store( private val indexes = CopyOnWriteArrayList() init { - storage.init(this) + storage.open(this) indexes.sortWith(compareBy { it.id }) } @@ -32,39 +33,53 @@ class Js5Store( indexes.add(index) } + fun putFile(indexId: Int, groupId: Int, id: Int, data: ByteArray) { + val group = index(indexId).group(groupId) + val exists = group.fileIds().contains(id) + if (exists) { + val file = group.file(id) + group.putFile(id, File(file.id, file.nameHash, data)) + return + } + group.putFile(id, File(id, 0, data)) + } + + fun save(): Boolean { + + return true + } + fun index(indexId: Int): Index = indexes.find { it.id == indexId }!! fun indexReferenceTableSize(indexId: Int): Int { var size = 0 - index(indexId).use { index -> + index(indexId).let { index -> index.groups().forEach { size += storage.loadReferenceTable(index, it.id).size } } return size } - fun groupReferenceTableSize(indexId: Int, groupName: String): Int { - val referenceTable = storage.loadReferenceTable(index(indexId), groupName) - return if (referenceTable.isEmpty()) 0 else referenceTable.size - 2 - } + fun groupReferenceTableSize( + indexId: Int, + groupName: String + ): Int = storage.loadReferenceTable(index(indexId), groupName).let { if (it.isEmpty()) 0 else it.size - 2 } - fun groupReferenceTableSize(indexId: Int, groupId: Int): Int { - val referenceTable = storage.loadReferenceTable(index(indexId), groupId) - return if (referenceTable.isEmpty()) 0 else referenceTable.size - 2 - } + fun groupReferenceTableSize( + indexId: Int, + groupId: Int + ): Int = storage.loadReferenceTable(index(indexId), groupId).let { if (it.isEmpty()) 0 else it.size - 2 } - fun groupReferenceTable(indexId: Int, groupId: Int): ByteArray { - if (indexId == Constants.MASTER_INDEX_ID) return storage.loadMasterReferenceTable(groupId) - return storage.loadReferenceTable(index(indexId), groupId) - } + fun groupReferenceTable( + indexId: Int, + groupId: Int + ): ByteArray = if (indexId == Constants.MASTER_INDEX_ID) storage.loadMasterReferenceTable(groupId) else storage.loadReferenceTable(index(indexId), groupId) - fun checksumsWithoutRSA(): ByteArray { - val header = ByteBuffer.allocate(indexes.size * 8) - indexes.forEach { - header.putInt(it.crc) - header.putInt(it.revision) + fun checksumsWithoutRSA(): ByteArray = ByteBuffer.allocate(indexes.size * 8).also { + indexes.forEach { index -> + it.putInt(index.crc) + it.putInt(index.revision) } - return header.array() - } + }.array() fun checksumsWithRSA(exponent: BigInteger, modulus: BigInteger): ByteArray { val header = ByteBuffer.allocate(indexes.size * 72 + 6) @@ -92,4 +107,4 @@ class Js5Store( } override fun close() = storage.close() -} \ No newline at end of file +} diff --git a/cache/src/main/kotlin/com/runetopic/cache/store/storage/IStorage.kt b/cache/src/main/kotlin/com/runetopic/cache/store/storage/Storage.kt similarity index 71% rename from cache/src/main/kotlin/com/runetopic/cache/store/storage/IStorage.kt rename to cache/src/main/kotlin/com/runetopic/cache/store/storage/Storage.kt index abbe06b..e823b2e 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/store/storage/IStorage.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/store/storage/Storage.kt @@ -9,10 +9,11 @@ import java.io.Flushable * @author Tyler Telis * @email */ -internal interface IStorage: Closeable, Flushable { - fun init(store: Js5Store) - fun open(indexId: Int, store: Js5Store) +internal interface Storage : Closeable, Flushable { + fun open(store: Js5Store) + fun read(indexId: Int, store: Js5Store) + fun write(indexId: Int, store: Js5Store) fun loadReferenceTable(index: Index, groupId: Int): ByteArray fun loadMasterReferenceTable(groupId: Int): ByteArray fun loadReferenceTable(index: Index, groupName: String): ByteArray -} \ No newline at end of file +} diff --git a/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/IDatFile.kt b/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/IDatFile.kt deleted file mode 100644 index 326094f..0000000 --- a/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/IDatFile.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.runetopic.cache.store.storage.js5 - -import com.runetopic.cache.hierarchy.ReferenceTable -import java.io.Closeable - -/** - * @author Tyler Telis - * @email - */ -internal interface IDatFile: Closeable { - fun readReferenceTable(id: Int, referenceTable: ReferenceTable): ByteArray -} \ No newline at end of file diff --git a/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/Js5DiskStorage.kt b/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/Js5DiskStorage.kt index 4bb641f..014fccd 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/Js5DiskStorage.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/Js5DiskStorage.kt @@ -1,14 +1,15 @@ package com.runetopic.cache.store.storage.js5 import com.github.michaelbull.logging.InlineLogger -import com.runetopic.cache.codec.ContainerCodec import com.runetopic.cache.hierarchy.index.Index import com.runetopic.cache.store.Constants import com.runetopic.cache.store.Js5Store -import com.runetopic.cache.store.storage.IStorage -import com.runetopic.cache.store.storage.js5.impl.DatFile -import com.runetopic.cache.store.storage.js5.impl.IdxFile -import com.runetopic.cryptography.toWhirlpool +import com.runetopic.cache.store.storage.Storage +import com.runetopic.cache.store.storage.js5.io.dat.DatFile +import com.runetopic.cache.store.storage.js5.io.dat.DatFileCodec +import com.runetopic.cache.store.storage.js5.io.dat.sector.DatIndexSector +import com.runetopic.cache.store.storage.js5.io.idx.IdxFileCodec +import com.runetopic.cache.store.storage.js5.io.idx.IdxFile import java.io.FileNotFoundException import java.nio.file.Path import java.util.concurrent.CopyOnWriteArrayList @@ -27,81 +28,78 @@ import kotlin.io.path.exists internal class Js5DiskStorage( private val path: Path, private val parallel: Boolean -) : IStorage { - private var masterIdxFile: IIdxFile - private var datFile: IDatFile +) : Storage { + private var masterIdxFile: IdxFileCodec + private var datFile: DatFileCodec private var idxFiles = CopyOnWriteArrayList() private val logger = InlineLogger() init { - val masterIndexFile = Path.of("${path}/${Constants.MAIN_FILE_255}") + val masterIndexFile = Path.of("$path/${Constants.MAIN_FILE_255}") if (masterIndexFile.exists().not()) { - throw FileNotFoundException("Missing ${Constants.MAIN_FILE_255} in directory ${path}/${Constants.MAIN_FILE_255}") + throw FileNotFoundException("Missing ${Constants.MAIN_FILE_255} in directory $path/${Constants.MAIN_FILE_255}") } - val datFile = Path.of("${path}/${Constants.MAIN_FILE_DAT}") + val datFile = Path.of("$path/${Constants.MAIN_FILE_DAT}") if (datFile.exists().not()) { - throw FileNotFoundException("Missing ${Constants.MAIN_FILE_DAT} in directory ${path}/${Constants.MAIN_FILE_DAT}") + throw FileNotFoundException("Missing ${Constants.MAIN_FILE_DAT} in directory $path/${Constants.MAIN_FILE_DAT}") } this.masterIdxFile = IdxFile(Constants.MASTER_INDEX_ID, masterIndexFile) this.datFile = DatFile(datFile) } - override fun init(store: Js5Store) { + override fun open(store: Js5Store) { logger.debug { "Opening $path for js5 indexes." } if (parallel) { val latch = CountDownLatch(masterIdxFile.validIndexCount()) val threads = Runtime.getRuntime().availableProcessors() val pool = Executors.newFixedThreadPool(if (threads >= 16) 8 else if (threads >= 8) 4 else 2) - (0 until masterIdxFile.validIndexCount()).forEach { + repeat(masterIdxFile.validIndexCount()) { pool.execute { - open(it, store) + read(it, store) latch.countDown() } } latch.await() pool.shutdown() - } else { - (0 until masterIdxFile.validIndexCount()).forEach { open(it, store) } - } + } else { repeat(masterIdxFile.validIndexCount()) { read(it, store) } } logger.debug { "Opened ${idxFiles.size} js5 indexes. (Allocated ${((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024)}MB)." } } - override fun open(indexId: Int, store: Js5Store) { - val indexTable = masterIdxFile.loadReferenceTable(indexId) + override fun read(indexId: Int, store: Js5Store) { + val indexReferenceTable = masterIdxFile.decode(indexId) idxFiles.add(getIdxFile(indexId)) - if (indexTable.exists().not()) { + if (indexReferenceTable.exists().not()) { store.addIndex(Index.default(indexId)) return } - val indexDatTable = datFile.readReferenceTable(masterIdxFile.id(), indexTable) - store.addIndex(decode(datFile, getIdxFile(indexId), indexDatTable.toWhirlpool(), ContainerCodec.decompress(indexDatTable))) + val data = datFile.decode(masterIdxFile.id(), indexReferenceTable) + store.addIndex(DatIndexSector(datFile, getIdxFile(indexId), data).decode()) } - override fun loadMasterReferenceTable(groupId: Int): ByteArray { - return datFile.readReferenceTable(Constants.MASTER_INDEX_ID, masterIdxFile.loadReferenceTable(groupId)) + override fun write(indexId: Int, store: Js5Store) { + val idk = masterIdxFile.encode(byteArrayOf()) + val data = datFile.encode(byteArrayOf()) + val index = store.index(indexId) + // val idk2 = DatIndexSector(datFile, getIdxFile(indexId), data).encode(index) } - override fun loadReferenceTable(index: Index, groupId: Int): ByteArray { - return datFile.readReferenceTable(index.id, getIdxFile(index.id).loadReferenceTable(groupId)) - } + override fun loadMasterReferenceTable(groupId: Int): ByteArray = datFile.decode(Constants.MASTER_INDEX_ID, masterIdxFile.decode(groupId)) - override fun loadReferenceTable(index: Index, groupName: String): ByteArray { - val group = index.group(groupName) - if (group.data.isEmpty()) return group.data - return datFile.readReferenceTable(index.id, getIdxFile(index.id).loadReferenceTable(group.id)) - } + override fun loadReferenceTable(index: Index, groupId: Int): ByteArray = datFile.decode(index.id, getIdxFile(index.id).decode(groupId)) - private fun getIdxFile(id: Int): IdxFile { - idxFiles.find { it.id() == id }?.let { return it } - return IdxFile(id, Path.of("$path/${Constants.MAIN_FILE_IDX}${id}")) + override fun loadReferenceTable(index: Index, groupName: String): ByteArray = index.group(groupName).let { + if (it.data.isEmpty()) return it.data + else datFile.decode(index.id, getIdxFile(index.id).decode(it.id)) } + private fun getIdxFile(id: Int): IdxFile = idxFiles.find { it.id() == id } ?: IdxFile(id, Path.of("$path/${Constants.MAIN_FILE_IDX}$id")) + override fun close() { masterIdxFile.close() datFile.close() @@ -111,4 +109,4 @@ internal class Js5DiskStorage( override fun flush() { TODO("Not yet implemented") } -} \ No newline at end of file +} diff --git a/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/Js5IndexDecoder.kt b/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/Js5IndexDecoder.kt deleted file mode 100644 index 47eae44..0000000 --- a/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/Js5IndexDecoder.kt +++ /dev/null @@ -1,247 +0,0 @@ -package com.runetopic.cache.store.storage.js5 - -import com.runetopic.cache.codec.Container -import com.runetopic.cache.codec.decompress -import com.runetopic.cache.exception.ProtocolException -import com.runetopic.cache.extension.readUnsignedByte -import com.runetopic.cache.extension.readUnsignedIntShortSmart -import com.runetopic.cache.extension.readUnsignedShort -import com.runetopic.cache.extension.toByteBuffer -import com.runetopic.cache.hierarchy.index.Index -import com.runetopic.cache.hierarchy.index.group.Group -import com.runetopic.cache.hierarchy.index.group.file.File -import java.nio.ByteBuffer -import java.util.zip.ZipException - -/** - * @author Jordan Abraham - */ -internal fun decode( - datFile: IDatFile, - idxFile: IIdxFile, - whirlpool: ByteArray, - decompressed: Container -): Index { - val buffer = decompressed.data.toByteBuffer() - val protocol = buffer.readUnsignedByte() - val revision = when { - protocol < 5 || protocol > 7 -> throw ProtocolException("Unhandled protocol $protocol") - protocol >= 6 -> buffer.int - else -> 0 - } - val hash = buffer.readUnsignedByte() - val count = if (protocol >= 7) buffer.readUnsignedIntShortSmart() else buffer.readUnsignedShort() - - val isNamed = (0x1 and hash) != 0 - val isUsingWhirlpool = (0x2 and hash) != 0 - - val groupIds = decodeGroupIds(count, buffer, protocol) - val maxGroupId = (groupIds.maxOrNull() ?: -1) + 1 - val groupNameHashes = decodeGroupNameHashes(maxGroupId, count, isNamed, groupIds, buffer) - val groupCrcs = decodeGroupCrcs(maxGroupId, count, groupIds, buffer) - val groupWhirlpools = decodeGroupWhirlpools(maxGroupId, isUsingWhirlpool, count, buffer, groupIds) - val groupRevisions = decodeGroupRevisions(maxGroupId, count, groupIds, buffer) - val groupFileIds = decodeGroupFileIds(maxGroupId, count, groupIds, buffer, protocol) - val fileIds = decodeFileIds(maxGroupId, groupFileIds, count, groupIds, buffer, protocol) - val fileNameHashes = decodeFileNameHashes(maxGroupId, groupFileIds, count, groupIds, buffer, isNamed) - - val groups = hashMapOf() - (0 until count).forEach { - val groupId = groupIds[it] - - val groupReferenceTableData = datFile.readReferenceTable(idxFile.id(), idxFile.loadReferenceTable(groupId)) - val data = if (groupReferenceTableData.isEmpty()) byteArrayOf() else try { - groupReferenceTableData.decompress() - } catch (exception: ZipException) { - groupReferenceTableData - } - - groups[groupId] = (Group( - groupId, - groupNameHashes[groupId], - groupCrcs[groupId], - groupWhirlpools[groupId], - groupRevisions[groupId], - intArrayOf(),//TODO - decodeFiles(fileIds, fileNameHashes, data, groupFileIds[groupId], groupId), - data - )) - } - return Index(idxFile.id(), decompressed.crc, whirlpool, decompressed.compression, protocol, revision, isNamed, groups) -} - -private fun decodeGroupIds( - count: Int, - buffer: ByteBuffer, - protocol: Int -): IntArray { - val groupIds = IntArray(count) - var offset = 0 - (0 until count).forEach { - groupIds[it] = if (protocol >= 7) { buffer.readUnsignedIntShortSmart() } else { buffer.readUnsignedShort() } - .let { id -> offset += id; offset } - } - return groupIds -} - -private fun decodeGroupFileIds( - maxGroupId: Int, - count: Int, - groupIds: IntArray, - buffer: ByteBuffer, - protocol: Int -): IntArray { - val groupFileIds = IntArray(maxGroupId) - (0 until count).forEach { - groupFileIds[groupIds[it]] = if (protocol >= 7) buffer.readUnsignedIntShortSmart() else buffer.readUnsignedShort() - } - return groupFileIds -} - -private fun decodeGroupRevisions( - maxGroupId: Int, - count: Int, - groupIds: IntArray, - buffer: ByteBuffer -): IntArray { - val revisions = IntArray(maxGroupId) - (0 until count).forEach { - revisions[groupIds[it]] = buffer.int - } - return revisions -} - -private fun decodeGroupWhirlpools( - maxGroupId: Int, - usesWhirlpool: Boolean, - count: Int, - buffer: ByteBuffer, - groupIds: IntArray -): Array { - val whirlpools = Array(maxGroupId) { ByteArray(64) } - if (usesWhirlpool.not()) return whirlpools - - (0 until count).forEach { - val whirlpool = ByteArray(64) - buffer.get(whirlpool) - whirlpools[groupIds[it]] = whirlpool - } - return whirlpools -} - -private fun decodeGroupCrcs( - maxGroupId: Int, - count: Int, - groupIds: IntArray, - buffer: ByteBuffer -): IntArray { - val crcs = IntArray(maxGroupId) - (0 until count).forEach { - crcs[groupIds[it]] = buffer.int - } - return crcs -} - -private fun decodeGroupNameHashes( - maxGroupId: Int, - count: Int, - isNamed: Boolean, - groupIds: IntArray, - buffer: ByteBuffer -): IntArray { - val nameHashes = IntArray(maxGroupId) { -1 } - if (isNamed.not()) return nameHashes - - (0 until count).forEach { - nameHashes[groupIds[it]] = buffer.int - } - return nameHashes -} - -private fun decodeFileIds( - maxGroupId: Int, - validFileIds: IntArray, - count: Int, - groupIds: IntArray, - buffer: ByteBuffer, - protocol: Int -): Array { - val fileIds = Array(maxGroupId) { IntArray(validFileIds[it]) } - (0 until count).forEach { - val groupId = groupIds[it] - var offset = 0 - (0 until validFileIds[groupId]).forEach { fileId -> - if (protocol >= 7) { buffer.readUnsignedIntShortSmart() } else { buffer.readUnsignedShort() } - .let { i -> offset += i; offset } - .also { fileIds[groupId][fileId] = offset } - } - } - return fileIds -} - -private fun decodeFileNameHashes( - maxGroupId: Int, - validFileIds: IntArray, - count: Int, - groupIds: IntArray, - buffer: ByteBuffer, - isNamed: Boolean -): Array { - val fileNameHashes = Array(maxGroupId) { IntArray(validFileIds[it]) } - if (isNamed) { - (0 until count).forEach { - val groupId = groupIds[it] - (0 until validFileIds[groupId]).forEach { fileId -> - fileNameHashes[groupId][fileId] = buffer.int - } - } - } - return fileNameHashes -} - -internal fun decodeFiles( - fileIds: Array, - fileNameHashes: Array, - data: ByteArray, - count: Int, - groupId: Int -): Map { - if (data.isEmpty()) return hashMapOf(Pair(0, File.DEFAULT)) - if (count <= 1) return hashMapOf(Pair(0, File(fileIds[groupId][0], fileNameHashes[groupId][0], data))) - - var position = data.size - val chunks = data[--position].toInt() and 0xFF - position -= chunks * (count * 4) - val buffer = data.toByteBuffer() - buffer.position(position) - val filesSizes = IntArray(count) - (0 until chunks).forEach { _ -> - var read = 0 - (0 until count).forEach { - read += buffer.int - filesSizes[it] += read - } - } - val filesDatas = Array(count) { byteArrayOf() } - (0 until count).forEach { - filesDatas[it] = ByteArray(filesSizes[it]) - filesSizes[it] = 0 - } - buffer.position(position) - var offset = 0 - (0 until chunks).forEach { _ -> - var read = 0 - (0 until count).forEach { - read += buffer.int - System.arraycopy(data, offset, filesDatas[it], filesSizes[it], read) - offset += read - filesSizes[it] += read - } - } - - val files = hashMapOf() - (0 until count).forEach { - files[it] = File(fileIds[groupId][it], fileNameHashes[groupId][it], filesDatas[it]) - } - return files -} \ No newline at end of file diff --git a/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/impl/DatFile.kt b/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/io/dat/DatFile.kt similarity index 72% rename from cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/impl/DatFile.kt rename to cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/io/dat/DatFile.kt index 6f47e38..c5e6a6a 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/impl/DatFile.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/io/dat/DatFile.kt @@ -1,14 +1,12 @@ -package com.runetopic.cache.store.storage.js5.impl +package com.runetopic.cache.store.storage.js5.io.dat import com.runetopic.cache.exception.DatFileException -import com.runetopic.cache.exception.EndOfDatFileException import com.runetopic.cache.extension.readUnsignedByte import com.runetopic.cache.extension.readUnsignedMedium import com.runetopic.cache.extension.readUnsignedShort import com.runetopic.cache.extension.toByteBuffer import com.runetopic.cache.hierarchy.ReferenceTable import com.runetopic.cache.store.Constants.DAT_SIZE -import com.runetopic.cache.store.storage.js5.IDatFile import java.io.RandomAccessFile import java.nio.ByteBuffer import java.nio.file.Path @@ -21,7 +19,7 @@ import java.nio.file.Path */ internal class DatFile( path: Path -): IDatFile { +) : DatFileCodec { private val datFile: RandomAccessFile = RandomAccessFile(path.toFile(), "rw") private val datBuffer = ByteArray(datFile.length().toInt()) @@ -29,7 +27,7 @@ internal class DatFile( datFile.readFully(datBuffer) } - override fun readReferenceTable( + override fun decode( id: Int, referenceTable: ReferenceTable ): ByteArray { @@ -37,32 +35,32 @@ internal class DatFile( val length = referenceTable.length val referenceTableId = referenceTable.id - if (validateSector(sector)) return byteArrayOf() - val buffer = ByteBuffer.allocate(length) var part = 0 var bytes = 0 while (length > bytes) { - if (sector == 0) { - throw EndOfDatFileException("Unexpected end of file. Id=[$id} Length=[$length]") - } + // validate sector + if (sector <= 0 || datBuffer.size / DAT_SIZE < sector) return byteArrayOf() - val offset = DAT_SIZE * sector + val position = DAT_SIZE * sector val large = referenceTableId > 0xFFFF val headerSize = if (large) 10 else 8 val blockSize = adjustBlockLength(length - bytes, headerSize) - val header = datBuffer.copyOfRange(offset, offset + headerSize + blockSize).toByteBuffer() + val totalSize = position + headerSize + blockSize + // validate the total size. + if (totalSize > datBuffer.size) return byteArrayOf() + val header = datBuffer.copyOfRange(position, totalSize).toByteBuffer() val currentReferenceTableId = if (large) header.int else header.readUnsignedShort() val currentPart = header.readUnsignedShort() val nextSector = header.readUnsignedMedium() val currentIndex = header.readUnsignedByte() if (referenceTableId != currentReferenceTableId || currentPart != part || id != currentIndex) { - throw DatFileException("DatFile mismatch Id={${currentIndex}} != {${id}}, ReferenceTableId={${currentReferenceTableId}} != {${referenceTableId}}, CurrentPart={${currentPart}} != {${part}}") + throw DatFileException("DatFile mismatch Id={$currentIndex} != {$id}, ReferenceTableId={$currentReferenceTableId} != {$referenceTableId}, CurrentPart={$currentPart} != {$part}") } - if (nextSector < 0 || datFile.length() / DAT_SIZE < nextSector) { + if (nextSector < 0 || datBuffer.size / DAT_SIZE < nextSector) { throw DatFileException("Invalid next sector $nextSector") } @@ -76,6 +74,9 @@ internal class DatFile( return buffer.array() } + override fun encode(data: ByteArray) { + } + private fun adjustBlockLength( blockLength: Int, headerLength: Int @@ -83,7 +84,7 @@ internal class DatFile( return if (blockLength <= DAT_SIZE - headerLength) blockLength else DAT_SIZE - headerLength } - private fun validateSector(sector: Int): Boolean = (sector <= 0L || datFile.length() / DAT_SIZE < sector) + private fun validateSector(sector: Int): Boolean = (sector <= 0 || datBuffer.size / DAT_SIZE < sector) override fun close() = datFile.close() -} \ No newline at end of file +} diff --git a/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/io/dat/DatFileCodec.kt b/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/io/dat/DatFileCodec.kt new file mode 100644 index 0000000..0b9e4d8 --- /dev/null +++ b/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/io/dat/DatFileCodec.kt @@ -0,0 +1,13 @@ +package com.runetopic.cache.store.storage.js5.io.dat + +import com.runetopic.cache.hierarchy.ReferenceTable +import java.io.Closeable + +/** + * @author Tyler Telis + * @email + */ +internal interface DatFileCodec : Closeable { + fun decode(id: Int, referenceTable: ReferenceTable): ByteArray + fun encode(data: ByteArray) +} diff --git a/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/io/dat/DatSectorCodec.kt b/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/io/dat/DatSectorCodec.kt new file mode 100644 index 0000000..5f6ec6e --- /dev/null +++ b/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/io/dat/DatSectorCodec.kt @@ -0,0 +1,9 @@ +package com.runetopic.cache.store.storage.js5.io.dat + +/** + * @author Jordan Abraham + */ +internal interface DatSectorCodec { + fun decode(): T + fun encode(override: T): ByteArray +} diff --git a/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/io/dat/sector/DatGroupSector.kt b/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/io/dat/sector/DatGroupSector.kt new file mode 100644 index 0000000..f7557bc --- /dev/null +++ b/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/io/dat/sector/DatGroupSector.kt @@ -0,0 +1,82 @@ +package com.runetopic.cache.store.storage.js5.io.dat.sector + +import com.runetopic.cache.extension.toByteBuffer +import com.runetopic.cache.hierarchy.index.group.file.File +import com.runetopic.cache.store.storage.js5.io.dat.DatSectorCodec + +/** + * @author Jordan Abraham + */ +data class DatGroupSector( + val fileIds: Array, + val fileNameHashes: Array, + val data: ByteArray, + val count: Int, + val groupId: Int +) : DatSectorCodec> { + + @OptIn(ExperimentalStdlibApi::class) + override fun decode(): Map { + if (data.isEmpty()) return hashMapOf(Pair(0, File.DEFAULT)) + if (count <= 1) return hashMapOf(Pair(0, File(fileIds[groupId][0], fileNameHashes[groupId][0], data))) + + var position = data.size + val chunks = data[--position].toInt() and 0xFF + position -= chunks * (count * 4) + val buffer = data.toByteBuffer() + buffer.position(position) + val filesSizes = IntArray(count) + repeat(chunks) { + var bytesRead = 0 + repeat(count) { + bytesRead += buffer.int + filesSizes[it] += bytesRead + } + } + val filesDatas = Array(count) { byteArrayOf() } + repeat(count) { + filesDatas[it] = ByteArray(filesSizes[it]) + filesSizes[it] = 0 + } + buffer.position(position) + var offset = 0 + repeat(chunks) { + var bytesRead = 0 + repeat(count) { + bytesRead += buffer.int + System.arraycopy(data, offset, filesDatas[it], filesSizes[it], bytesRead) + offset += bytesRead + filesSizes[it] += bytesRead + } + } + return buildMap { repeat(count) { put(it, File(fileIds[groupId][it], fileNameHashes[groupId][it], filesDatas[it])) } } + } + + override fun encode(override: Map): ByteArray { + TODO("Not yet implemented") + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as DatGroupSector + + if (!fileIds.contentDeepEquals(other.fileIds)) return false + if (!fileNameHashes.contentDeepEquals(other.fileNameHashes)) return false + if (!data.contentEquals(other.data)) return false + if (count != other.count) return false + if (groupId != other.groupId) return false + + return true + } + + override fun hashCode(): Int { + var result = fileIds.contentDeepHashCode() + result = 31 * result + fileNameHashes.contentDeepHashCode() + result = 31 * result + data.contentHashCode() + result = 31 * result + count + result = 31 * result + groupId + return result + } +} diff --git a/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/io/dat/sector/DatIndexSector.kt b/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/io/dat/sector/DatIndexSector.kt new file mode 100644 index 0000000..fc15848 --- /dev/null +++ b/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/io/dat/sector/DatIndexSector.kt @@ -0,0 +1,433 @@ +package com.runetopic.cache.store.storage.js5.io.dat.sector + +import com.runetopic.cache.codec.ContainerCodec +import com.runetopic.cache.codec.decompress +import com.runetopic.cache.exception.ProtocolException +import com.runetopic.cache.extension.* +import com.runetopic.cache.hierarchy.index.Index +import com.runetopic.cache.hierarchy.index.group.Group +import com.runetopic.cache.store.storage.js5.io.dat.DatFileCodec +import com.runetopic.cache.store.storage.js5.io.dat.DatSectorCodec +import com.runetopic.cache.store.storage.js5.io.idx.IdxFileCodec +import com.runetopic.cryptography.toWhirlpool +import java.nio.ByteBuffer +import java.util.zip.ZipException + +/** + * @author Jordan Abraham + */ +internal data class DatIndexSector( + val datFile: DatFileCodec, + val idxFile: IdxFileCodec, + val data: ByteArray +) : DatSectorCodec { + + override fun decode(): Index { + val decompressed = ContainerCodec.decompress(data) + val buffer = decompressed.data.toByteBuffer() + val protocol = buffer.readUnsignedByte() + val revision = when { + protocol < 5 || protocol > 7 -> throw ProtocolException("Unhandled protocol $protocol") + protocol >= 6 -> buffer.int + else -> 0 + } + val mask = buffer.readUnsignedByte() + val count = if (protocol >= 7) buffer.readUnsignedIntShortSmart() else buffer.readUnsignedShort() + + val isNamed = (0x1 and mask) != 0 + val isUsingWhirlpool = (0x2 and mask) != 0 + + val groupIds = decodeGroupIds(count, buffer, protocol) + val maxGroupId = (groupIds.maxOrNull() ?: -1) + 1 + val groupNameHashes = decodeGroupNameHashes(maxGroupId, count, isNamed, groupIds, buffer) + val groupCrcs = decodeGroupCrcs(maxGroupId, count, groupIds, buffer) + val groupWhirlpools = decodeGroupWhirlpools(maxGroupId, isUsingWhirlpool, count, buffer, groupIds) + val groupRevisions = decodeGroupRevisions(maxGroupId, count, groupIds, buffer) + val groupFileSizes = decodeGroupFileSizes(maxGroupId, count, groupIds, buffer, protocol) + val groupFileIds = decodeGroupFileIds(maxGroupId, groupFileSizes, count, groupIds, buffer, protocol) + val groupFileNameHashes = decodeGroupFileNameHashes(maxGroupId, groupFileSizes, count, groupIds, buffer, isNamed) + + val groups = hashMapOf() + repeat(count) { + val groupId = groupIds[it] + + val groupReferenceTableData = datFile.decode(idxFile.id(), idxFile.decode(groupId)) + val data = if (groupReferenceTableData.isEmpty()) byteArrayOf() else try { + groupReferenceTableData.decompress() + } catch (exception: ZipException) { + groupReferenceTableData + } + + val groupSector = DatGroupSector( + groupFileIds, + groupFileNameHashes, + data, + groupFileSizes[groupId], + groupId + ) + + groups[groupId] = Group( + groupId, + groupNameHashes[groupId], + groupCrcs[groupId], + groupWhirlpools[groupId], + groupRevisions[groupId], + intArrayOf(), // TODO + groupSector.decode().toMutableMap(), + data + ) + } + + return Index( + idxFile.id(), + decompressed.crc, + data.toWhirlpool(), + decompressed.compression, + protocol, + revision, + isNamed, + isUsingWhirlpool, + groups + ) + } + + override fun encode(override: Index): ByteArray { + val count = override.groups().size + val header = ByteBuffer.allocate(if (override.protocol >= 7) if (count >= Short.MAX_VALUE) 10 else 8 else if (override.protocol >= 6) 8 else 4) + header.put(override.protocol.toByte()) + if (override.protocol >= 6) { + header.putInt(override.revision) + } + + val mask = when { + override.isNamed -> 0x1 + override.isUsingWhirlpool -> 0x2 + else -> 0x0 + } + header.put(mask.toByte()) + + if (override.protocol >= 7) header.putIntShortSmart(count) else header.putShort(count.toShort()) + + val ids = override.groups().stream().map { it.id }.toList().toIntArray() + val max = (ids.maxOrNull() ?: -1) + 1 + val nameHashes = IntArray(max) { override.group(it).nameHash } + val crcs = IntArray(max) { override.group(it).crc } + val whirlpools = Array(max) { override.group(it).whirlpool } + val revisions = IntArray(max) { override.group(it).revision } + val fileSizes = IntArray(max) { override.group(it).fileIds().size } + val fileIds = Array(max) { override.groups().stream().filter { group -> group.id == it }.findFirst().takeIf { it.isPresent }?.get()?.fileIds()?.map { it }?.toList()?.toIntArray() ?: intArrayOf() } + val fileNameHashes = Array(max) { override.groups().stream().filter { group -> group.id == it }.findFirst().takeIf { it.isPresent }?.get()?.files()?.map { it.nameHash }?.toList()?.toIntArray() ?: intArrayOf() } + + val groupIds = encodeGroupIds(count, override.protocol, ids) + val groupNameHashes = encodeGroupNameHashes(count, override.isNamed, ids, nameHashes) + val groupCrcs = encodeGroupCrcs(count, ids, crcs) + val groupWhirlpools = encodeGroupWhirlpools(count, ids, override.isUsingWhirlpool, whirlpools) + val groupRevisions = encodeGroupRevisions(count, ids, revisions) + val groupFileSizes = encodeGroupFileSizes(count, ids, override.protocol, fileSizes) + val groupFileIds = encodeGroupFileIds(count, ids, override.protocol, fileSizes, fileIds) + val groupFileNameHashes = encodeGroupFileNameHashes(count, ids, fileSizes, override.isNamed, fileNameHashes) + + val buffer = ByteBuffer.allocate( + /**/header.position() + + groupIds.position() + + groupNameHashes.position() + + groupCrcs.position() + + groupWhirlpools.position() + + groupRevisions.position() + + groupFileSizes.position() + + groupFileIds.position() + + groupFileNameHashes.position() + ) + + buffer.put(header.array()) + buffer.put(groupIds.array()) + buffer.put(groupNameHashes.array()) + buffer.put(groupCrcs.array()) + buffer.put(groupWhirlpools.array()) + buffer.put(groupRevisions.array()) + buffer.put(groupFileSizes.array()) + buffer.put(groupFileIds.array()) + buffer.put(groupFileNameHashes.array()) + + val compressed = ContainerCodec.compress( + override.compression, + override.revision, + buffer.array(), + intArrayOf() // TODO + ) + return compressed.array() + } + + fun decodeGroupIds( + count: Int, + buffer: ByteBuffer, + protocol: Int + ): IntArray { + return IntArray(count).also { + repeat(count) { index -> + it[index] = (if (protocol >= 7) buffer.readUnsignedIntShortSmart() else buffer.readUnsignedShort()) + if (index == 0) 0 else it[index - 1] + } + } + } + + fun encodeGroupIds( + count: Int, + protocol: Int, + groupIds: IntArray + ): ByteBuffer { + // TODO This buffer needs to be allocated properly for protocol >= 7 + return ByteBuffer.allocate(calc(count, groupIds, protocol)).also { + repeat(count) { index -> + val value = groupIds[index] - if (index == 0) 0 else groupIds[index - 1] + if (protocol >= 7) it.putIntShortSmart(value) else it.putShort(value.toShort()) + } + } + } + + fun calc( + count: Int, + groupIds: IntArray, + protocol: Int + ): Int { + var bytes = 0 + repeat(count) { + val value = groupIds[it] - if (it == 0) 0 else groupIds[it - 1] + bytes += if (protocol >= 7) if (value >= Short.MAX_VALUE) 4 else 2 else 2 + } + return bytes + } + + fun decodeGroupFileSizes( + maxGroupId: Int, + count: Int, + groupIds: IntArray, + buffer: ByteBuffer, + protocol: Int + ): IntArray { + return IntArray(maxGroupId).also { + repeat(count) { index -> + it[groupIds[index]] = if (protocol >= 7) buffer.readUnsignedIntShortSmart() else buffer.readUnsignedShort() + } + } + } + + fun encodeGroupFileSizes( + count: Int, + groupIds: IntArray, + protocol: Int, + groupFileSizes: IntArray + ): ByteBuffer { + // TODO This buffer needs to be allocated properly for protocol >= 7 + return ByteBuffer.allocate(groupIds.sumOf { Short.SIZE_BYTES }).also { + repeat(count) { index -> + if (protocol >= 7) it.putIntShortSmart(groupFileSizes[groupIds[index]]) else it.putShort(groupFileSizes[groupIds[index]].toShort()) + } + } + } + + fun decodeGroupRevisions( + maxGroupId: Int, + count: Int, + groupIds: IntArray, + buffer: ByteBuffer + ): IntArray { + return IntArray(maxGroupId).also { + repeat(count) { index -> + it[groupIds[index]] = buffer.int + } + } + } + + fun encodeGroupRevisions( + count: Int, + groupIds: IntArray, + revisions: IntArray + ): ByteBuffer { + return ByteBuffer.allocate(count * Int.SIZE_BYTES).also { + repeat(count) { index -> + it.putInt(revisions[groupIds[index]]) + } + } + } + + fun decodeGroupWhirlpools( + maxGroupId: Int, + usesWhirlpool: Boolean, + count: Int, + buffer: ByteBuffer, + groupIds: IntArray + ): Array { + return Array(maxGroupId) { ByteArray(64) }.also { + if (usesWhirlpool) { + repeat(count) { index -> + it[groupIds[index]] = ByteArray(64).also { b -> buffer.get(b) } + } + } + } + } + + fun encodeGroupWhirlpools( + count: Int, + groupIds: IntArray, + usesWhirlpool: Boolean, + whirlpools: Array + ): ByteBuffer { + return if (usesWhirlpool.not()) ByteBuffer.allocate(0) else ByteBuffer.allocate(count * 64).also { + repeat(count) { index -> + it.put(whirlpools[groupIds[index]]) + } + } + } + + fun decodeGroupCrcs( + maxGroupId: Int, + count: Int, + groupIds: IntArray, + buffer: ByteBuffer + ): IntArray { + return IntArray(maxGroupId).also { + repeat(count) { index -> + it[groupIds[index]] = buffer.int + } + } + } + + fun encodeGroupCrcs( + count: Int, + groupIds: IntArray, + crcs: IntArray + ): ByteBuffer { + return ByteBuffer.allocate(count * Int.SIZE_BYTES).also { + repeat(count) { index -> + it.putInt(crcs[groupIds[index]]) + } + } + } + + fun decodeGroupNameHashes( + maxGroupId: Int, + count: Int, + isNamed: Boolean, + groupIds: IntArray, + buffer: ByteBuffer + ): IntArray { + return IntArray(maxGroupId) { -1 }.also { + if (isNamed) { + repeat(count) { index -> + it[groupIds[index]] = buffer.int + } + } + } + } + + fun encodeGroupNameHashes( + count: Int, + isNamed: Boolean, + groupIds: IntArray, + nameHashes: IntArray + ): ByteBuffer { + return if (isNamed.not()) ByteBuffer.allocate(0) else ByteBuffer.allocate(count * Int.SIZE_BYTES).also { + repeat(count) { index -> + it.putInt(nameHashes[groupIds[index]]) + } + } + } + + fun decodeGroupFileIds( + maxGroupId: Int, + groupFileSizes: IntArray, + count: Int, + groupIds: IntArray, + buffer: ByteBuffer, + protocol: Int + ): Array { + return Array(maxGroupId) { IntArray(groupFileSizes[it]) }.also { + repeat(count) { index -> + groupIds[index].let { groupId -> + repeat(groupFileSizes[groupId]) { fileId -> + it[groupId][fileId] = (if (protocol >= 7) buffer.readUnsignedIntShortSmart() else buffer.readUnsignedShort()) + if (fileId == 0) 0 else it[groupId][fileId - 1] + } + } + } + } + } + + fun encodeGroupFileIds( + count: Int, + groupIds: IntArray, + protocol: Int, + groupFileSizes: IntArray, + fileIds: Array + ): ByteBuffer { + // TODO This buffer needs to be allocated properly for protocol >= 7 + return ByteBuffer.allocate(groupFileSizes.sumOf { it * Short.SIZE_BYTES }).also { + repeat(count) { index -> + groupIds[index].let { groupId -> + repeat(groupFileSizes[groupId]) { fileId -> + val value = fileIds[groupId][fileId] - if (fileId == 0) 0 else fileIds[groupId][fileId - 1] + if (protocol >= 7) it.putIntShortSmart(value) else it.putShort(value.toShort()) + } + } + } + } + } + + fun decodeGroupFileNameHashes( + maxGroupId: Int, + groupFileSizes: IntArray, + count: Int, + groupIds: IntArray, + buffer: ByteBuffer, + isNamed: Boolean + ): Array { + return Array(maxGroupId) { IntArray(groupFileSizes[it]) { -1 } }.also { + if (isNamed) { + repeat(count) { index -> + groupIds[index].let { groupId -> + repeat(groupFileSizes[groupId]) { fileId -> + it[groupId][fileId] = buffer.int + } + } + } + } + } + } + + fun encodeGroupFileNameHashes( + count: Int, + groupIds: IntArray, + groupFileSizes: IntArray, + isNamed: Boolean, + fileNameHashes: Array + ): ByteBuffer { + return if (isNamed.not()) ByteBuffer.allocate(0) else ByteBuffer.allocate(groupFileSizes.sum() * Int.SIZE_BYTES).also { + repeat(count) { index -> + groupIds[index].let { groupId -> + repeat(groupFileSizes[groupId]) { fileId -> + it.putInt(fileNameHashes[groupId][fileId]) + } + } + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as DatIndexSector + + if (datFile != other.datFile) return false + if (idxFile != other.idxFile) return false + if (!data.contentEquals(other.data)) return false + + return true + } + + override fun hashCode(): Int { + var result = datFile.hashCode() + result = 31 * result + idxFile.hashCode() + result = 31 * result + data.contentHashCode() + return result + } +} diff --git a/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/impl/IdxFile.kt b/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/io/idx/IdxFile.kt similarity index 68% rename from cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/impl/IdxFile.kt rename to cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/io/idx/IdxFile.kt index 51a0094..5e507ce 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/impl/IdxFile.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/io/idx/IdxFile.kt @@ -1,12 +1,10 @@ -package com.runetopic.cache.store.storage.js5.impl +package com.runetopic.cache.store.storage.js5.io.idx import com.runetopic.cache.exception.IdxFileException import com.runetopic.cache.extension.readUnsignedMedium import com.runetopic.cache.extension.toByteBuffer import com.runetopic.cache.hierarchy.ReferenceTable -import com.runetopic.cache.store.Constants import com.runetopic.cache.store.Constants.IDX_SIZE -import com.runetopic.cache.store.storage.js5.IIdxFile import java.io.RandomAccessFile import java.nio.file.Path import kotlin.io.path.ExperimentalPathApi @@ -21,17 +19,16 @@ import kotlin.io.path.fileSize internal class IdxFile( private val id: Int, private val path: Path -) : IIdxFile { +) : IdxFileCodec { private val idxFile: RandomAccessFile = RandomAccessFile(path.toFile(), "rw") - private val idxBuffer = ByteArray(idxFile.length().toInt()) + private val idxBuffer = ByteArray(idxFile.length().toInt()).also { idxFile.readFully(it) } - init { - idxFile.readFully(idxBuffer) - } - - override fun loadReferenceTable(id: Int): ReferenceTable { - val offset = id * IDX_SIZE - val buffer = idxBuffer.copyOfRange(offset, offset + IDX_SIZE).toByteBuffer() + override fun decode(id: Int): ReferenceTable { + if (idxBuffer.size < id * IDX_SIZE + 6) { + return ReferenceTable(id, 0, 0) + } + val position = id * IDX_SIZE + val buffer = idxBuffer.copyOfRange(position, position + IDX_SIZE).toByteBuffer() val length = buffer.readUnsignedMedium() val sector = buffer.readUnsignedMedium() if (length < 0) { @@ -40,8 +37,12 @@ internal class IdxFile( return ReferenceTable(id, sector, length) } + override fun encode(data: ByteArray) { + TODO("Not yet implemented") + } + @OptIn(ExperimentalPathApi::class) override fun validIndexCount(): Int = path.fileSize().toInt() / IDX_SIZE override fun id(): Int = id override fun close() = idxFile.close() -} \ No newline at end of file +} diff --git a/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/IIdxFile.kt b/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/io/idx/IdxFileCodec.kt similarity index 52% rename from cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/IIdxFile.kt rename to cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/io/idx/IdxFileCodec.kt index e39adfe..771819e 100644 --- a/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/IIdxFile.kt +++ b/cache/src/main/kotlin/com/runetopic/cache/store/storage/js5/io/idx/IdxFileCodec.kt @@ -1,4 +1,4 @@ -package com.runetopic.cache.store.storage.js5 +package com.runetopic.cache.store.storage.js5.io.idx import com.runetopic.cache.hierarchy.ReferenceTable import java.io.Closeable @@ -7,8 +7,9 @@ import java.io.Closeable * @author Tyler Telis * @email */ -internal interface IIdxFile: Closeable { - fun loadReferenceTable(id: Int): ReferenceTable +internal interface IdxFileCodec : Closeable { + fun decode(id: Int): ReferenceTable + fun encode(data: ByteArray) fun validIndexCount(): Int fun id(): Int -} \ No newline at end of file +} diff --git a/cache/src/test/kotlin/com/runetopic/cache/store/Js5StoreTest.kt b/cache/src/test/kotlin/com/runetopic/cache/store/Js5StoreTest.kt new file mode 100644 index 0000000..11d0217 --- /dev/null +++ b/cache/src/test/kotlin/com/runetopic/cache/store/Js5StoreTest.kt @@ -0,0 +1,4 @@ +package com.runetopic.cache.store + +class Js5StoreTest { +} diff --git a/cache/src/test/kotlin/com/runetopic/cache/store/storage/js5/io/dat/DatSectorTest.kt b/cache/src/test/kotlin/com/runetopic/cache/store/storage/js5/io/dat/DatSectorTest.kt new file mode 100644 index 0000000..f0a1a6d --- /dev/null +++ b/cache/src/test/kotlin/com/runetopic/cache/store/storage/js5/io/dat/DatSectorTest.kt @@ -0,0 +1,292 @@ +package com.runetopic.cache.store.storage.js5.io.dat + +import com.runetopic.cache.store.storage.js5.io.dat.sector.DatIndexSector +import com.runetopic.cache.store.storage.js5.io.idx.IdxFile +import io.mockk.confirmVerified +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import java.nio.ByteBuffer +import java.nio.file.Path +import java.util.stream.IntStream +import kotlin.random.Random +import kotlin.random.nextInt +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * @author Jordan Abraham + */ +class DatSectorTest { + + @Test + fun `test decode and encode`() { + val indexSector = mockk() + + every { indexSector.datFile } returns DatFile(Path.of("src", "test", "resources", "./main_file_cache.dat2")) + every { indexSector.idxFile } returns IdxFile(10, Path.of("src", "test", "resources", "./main_file_cache.idx10")) + every { indexSector.data } returns byteArrayOf(0, 0, 0, 0, 30, 6, 0, 0, 0, 5, 1, 0, 1, 0, 1, 74, -4, 115, -83, 120, 17, 126, 78, 0, 0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0) + + every { indexSector.decode() } answers { callOriginal() } + every { indexSector.decodeGroupIds(any(), any(), any()) } answers { callOriginal() } + every { indexSector.decodeGroupNameHashes(any(), any(), any(), any(), any()) } answers { callOriginal() } + every { indexSector.decodeGroupCrcs(any(), any(), any(), any()) } answers { callOriginal() } + every { indexSector.decodeGroupWhirlpools(any(), any(), any(), any(), any()) } answers { callOriginal() } + every { indexSector.decodeGroupRevisions(any(), any(), any(), any()) } answers { callOriginal() } + every { indexSector.decodeGroupFileSizes(any(), any(), any(), any(), any()) } answers { callOriginal() } + every { indexSector.decodeGroupFileIds(any(), any(), any(), any(), any(), any()) } answers { callOriginal() } + every { indexSector.decodeGroupFileNameHashes(any(), any(), any(), any(), any(), any()) } answers { callOriginal() } + + every { indexSector.calc(any(), any(), any()) } answers { callOriginal() } + every { indexSector.encode(any()) } answers { callOriginal() } + every { indexSector.encodeGroupIds(any(), any(), any()) } answers { callOriginal() } + every { indexSector.encodeGroupNameHashes(any(), any(), any(), any()) } answers { callOriginal() } + every { indexSector.encodeGroupCrcs(any(), any(), any()) } answers { callOriginal() } + every { indexSector.encodeGroupWhirlpools(any(), any(), any(), any()) } answers { callOriginal() } + every { indexSector.encodeGroupRevisions(any(), any(), any()) } answers { callOriginal() } + every { indexSector.encodeGroupFileSizes(any(), any(), any(), any()) } answers { callOriginal() } + every { indexSector.encodeGroupFileIds(any(), any(), any(), any(), any()) } answers { callOriginal() } + every { indexSector.encodeGroupFileNameHashes(any(), any(), any(), any(), any()) } answers { callOriginal() } + + val decoded = indexSector.decode() + val encoded = indexSector.encode(decoded) + + assertEquals( + indexSector.data.contentToString(), + encoded.contentToString() + ) + + verify(exactly = 1) { indexSector.decode() } + verify(atLeast = 1) { indexSector.decodeGroupIds(any(), any(), any()) } + verify(atLeast = 1) { indexSector.decodeGroupNameHashes(any(), any(), any(), any(), any()) } + verify(atLeast = 1) { indexSector.decodeGroupCrcs(any(), any(), any(), any()) } + verify(atLeast = 1) { indexSector.decodeGroupWhirlpools(any(), any(), any(), any(), any()) } + verify(atLeast = 1) { indexSector.decodeGroupRevisions(any(), any(), any(), any()) } + verify(atLeast = 1) { indexSector.decodeGroupFileSizes(any(), any(), any(), any(), any()) } + verify(atLeast = 1) { indexSector.decodeGroupFileIds(any(), any(), any(), any(), any(), any()) } + verify(atLeast = 1) { indexSector.decodeGroupFileNameHashes(any(), any(), any(), any(), any(), any()) } + + verify(exactly = 1) { indexSector.encode(decoded) } + verify(atLeast = 1) { indexSector.calc(any(), any(), any()) } + verify(atLeast = 1) { indexSector.encodeGroupIds(any(), any(), any()) } + verify(atLeast = 1) { indexSector.encodeGroupNameHashes(any(), any(), any(), any()) } + verify(atLeast = 1) { indexSector.encodeGroupCrcs(any(), any(), any()) } + verify(atLeast = 1) { indexSector.encodeGroupWhirlpools(any(), any(), any(), any()) } + verify(atLeast = 1) { indexSector.encodeGroupRevisions(any(), any(), any()) } + verify(atLeast = 1) { indexSector.encodeGroupFileSizes(any(), any(), any(), any()) } + verify(atLeast = 1) { indexSector.encodeGroupFileIds(any(), any(), any(), any(), any()) } + verify(atLeast = 1) { indexSector.encodeGroupFileNameHashes(any(), any(), any(), any(), any()) } + + verify(atLeast = 1) { indexSector.datFile } + verify(atLeast = 1) { indexSector.idxFile } + verify(atLeast = 1) { indexSector.data } + + confirmVerified(indexSector) + } + + @Test + fun `test file ids protocol 6`() { + val count = Random.nextInt(10..50) + val groupIds = IntStream.generate { Random.nextInt(count..count * 2) }.distinct().limit(count.toLong()).sorted().toArray() + val maxGroupId = (groupIds.maxOrNull() ?: -1) + 1 + val groupFileSizes = IntArray(maxGroupId) { if (it in groupIds) Random.nextInt(1..10) else 0 } + val protocol = 7 + + val indexSector = mockk() + every { indexSector.decodeGroupFileIds(any(), any(), any(), any(), any(), any()) } answers { callOriginal() } + every { indexSector.encodeGroupFileIds(any(), any(), any(), any(), any()) } answers { callOriginal() } + + val buffer = ByteBuffer.wrap(Random.nextBytes(groupFileSizes.sumOf { it * Short.SIZE_BYTES })) + + val decoded = indexSector.decodeGroupFileIds(maxGroupId, groupFileSizes, count, groupIds, buffer, protocol) + val encoded = indexSector.encodeGroupFileIds(count, groupIds, protocol, groupFileSizes, decoded) + + assertEquals( + buffer.array().contentToString(), + encoded.array().contentToString() + ) + + verify(exactly = 1) { indexSector.decodeGroupFileIds(maxGroupId, groupFileSizes, count, groupIds, buffer, protocol) } + verify(exactly = 1) { indexSector.encodeGroupFileIds(count, groupIds, protocol, groupFileSizes, decoded) } + confirmVerified(indexSector) + } + + @Test + fun `test ids protocol 6`() { + val count = Random.nextInt(10..50) + val groupIds = IntStream.generate { Random.nextInt(count..count * 2) }.distinct().limit(count.toLong()).sorted().toArray() + val protocol = 7 + + val indexSector = mockk() + every { indexSector.decodeGroupIds(any(), any(), any()) } answers { callOriginal() } + every { indexSector.encodeGroupIds(any(), any(), any()) } answers { callOriginal() } + every { indexSector.calc(any(), any(), any()) } answers { callOriginal() } + + val buffer = ByteBuffer.wrap(Random.nextBytes(indexSector.calc(count, groupIds, protocol))) + + val decoded = indexSector.decodeGroupIds(count, buffer, protocol) + val encoded = indexSector.encodeGroupIds(count, protocol, decoded) + + assertEquals( + buffer.array().contentToString(), + encoded.array().contentToString() + ) + + verify(exactly = 1) { indexSector.decodeGroupIds(count, buffer, protocol) } + verify(exactly = 1) { indexSector.encodeGroupIds(count, protocol, decoded) } + verify(atLeast = 1) { indexSector.calc(count, groupIds, protocol) } + // confirmVerified(indexSector) + } + + @Test + fun `test fileSizes protocol 6`() { + val count = Random.nextInt(10..50) + val groupIds = IntStream.generate { Random.nextInt(count..count * 2) }.distinct().limit(count.toLong()).sorted().toArray() + val maxGroupId = (groupIds.maxOrNull() ?: -1) + 1 + val protocol = 6 + + val indexSector = mockk() + every { indexSector.decodeGroupFileSizes(any(), any(), any(), any(), any()) } answers { callOriginal() } + every { indexSector.encodeGroupFileSizes(any(), any(), any(), any()) } answers { callOriginal() } + + val buffer = ByteBuffer.wrap(Random.nextBytes(groupIds.sumOf { (if (protocol >= 7) if (it >= Short.MAX_VALUE) 4 else 2 else 2).toInt() })) + + val decoded = indexSector.decodeGroupFileSizes(maxGroupId, count, groupIds, buffer, protocol) + val encoded = indexSector.encodeGroupFileSizes(count, groupIds, protocol, decoded) + + assertEquals( + buffer.array().contentToString(), + encoded.array().contentToString() + ) + + verify(exactly = 1) { indexSector.decodeGroupFileSizes(maxGroupId, count, groupIds, buffer, protocol) } + verify(exactly = 1) { indexSector.encodeGroupFileSizes(count, groupIds, protocol, decoded) } + confirmVerified(indexSector) + } + + @Test + fun `test file nameHashes`() { + val count = Random.nextInt(10..50) + val groupIds = IntStream.generate { Random.nextInt(count..count * 2) }.distinct().limit(count.toLong()).sorted().toArray() + val maxGroupId = (groupIds.maxOrNull() ?: -1) + 1 + val validFileIds = IntArray(maxGroupId) { if (it in groupIds) Random.nextInt(1..10) else 0 } + + val indexSector = mockk() + every { indexSector.decodeGroupFileNameHashes(any(), any(), any(), any(), any(), any()) } answers { callOriginal() } + every { indexSector.encodeGroupFileNameHashes(any(), any(), any(), any(), any()) } answers { callOriginal() } + + val buffer = ByteBuffer.wrap(Random.nextBytes(validFileIds.sum() * Int.SIZE_BYTES)) + + val decoded = indexSector.decodeGroupFileNameHashes(maxGroupId, validFileIds, count, groupIds, buffer, true) + val encoded = indexSector.encodeGroupFileNameHashes(count, groupIds, validFileIds, true, decoded) + + assertEquals( + buffer.array().contentToString(), + encoded.array().contentToString() + ) + + verify(exactly = 1) { indexSector.decodeGroupFileNameHashes(maxGroupId, validFileIds, count, groupIds, buffer, true) } + verify(exactly = 1) { indexSector.encodeGroupFileNameHashes(count, groupIds, validFileIds, true, decoded) } + confirmVerified(indexSector) + } + + @Test + fun `test whirlpools`() { + val count = Random.nextInt(10..50) + val groupIds = IntStream.generate { Random.nextInt(count..count * 2) }.distinct().limit(count.toLong()).sorted().toArray() + val maxGroupId = (groupIds.maxOrNull() ?: -1) + 1 + + val buffer = ByteBuffer.wrap(Random.nextBytes(count * 64)) + + val indexSector = mockk() + every { indexSector.decodeGroupWhirlpools(any(), any(), any(), any(), any()) } answers { callOriginal() } + every { indexSector.encodeGroupWhirlpools(any(), any(), any(), any()) } answers { callOriginal() } + + val decoded = indexSector.decodeGroupWhirlpools(maxGroupId, true, count, buffer, groupIds) + val encoded = indexSector.encodeGroupWhirlpools(count, groupIds, true, decoded) + + assertEquals( + buffer.array().contentToString(), + encoded.array().contentToString() + ) + + verify(exactly = 1) { indexSector.decodeGroupWhirlpools(maxGroupId, true, count, buffer, groupIds) } + verify(exactly = 1) { indexSector.encodeGroupWhirlpools(count, groupIds, true, decoded) } + confirmVerified(indexSector) + } + + @Test + fun `test crcs`() { + val count = Random.nextInt(10..50) + val groupIds = IntStream.generate { Random.nextInt(count..count * 2) }.distinct().limit(count.toLong()).sorted().toArray() + val maxGroupId = (groupIds.maxOrNull() ?: -1) + 1 + + val buffer = ByteBuffer.wrap(Random.nextBytes(count * Int.SIZE_BYTES)) + + val indexSector = mockk() + every { indexSector.decodeGroupCrcs(any(), any(), any(), any()) } answers { callOriginal() } + every { indexSector.encodeGroupCrcs(any(), any(), any()) } answers { callOriginal() } + + val decoded = indexSector.decodeGroupCrcs(maxGroupId, count, groupIds, buffer) + val encoded = indexSector.encodeGroupCrcs(count, groupIds, decoded) + + assertEquals( + buffer.array().contentToString(), + encoded.array().contentToString() + ) + + verify(exactly = 1) { indexSector.decodeGroupCrcs(maxGroupId, count, groupIds, buffer) } + verify(exactly = 1) { indexSector.encodeGroupCrcs(count, groupIds, decoded) } + confirmVerified(indexSector) + } + + @Test + fun `test nameHashes`() { + val count = Random.nextInt(10..50) + val groupIds = IntStream.generate { Random.nextInt(count..count * 2) }.distinct().limit(count.toLong()).sorted().toArray() + val maxGroupId = (groupIds.maxOrNull() ?: -1) + 1 + + val buffer = ByteBuffer.wrap(Random.nextBytes(count * Int.SIZE_BYTES)) + + val indexSector = mockk() + every { indexSector.decodeGroupNameHashes(any(), any(), any(), any(), any()) } answers { callOriginal() } + every { indexSector.encodeGroupNameHashes(any(), any(), any(), any()) } answers { callOriginal() } + + val decoded = indexSector.decodeGroupNameHashes(maxGroupId, count, true, groupIds, buffer) + val encoded = indexSector.encodeGroupNameHashes(count, true, groupIds, decoded) + + assertEquals( + buffer.array().contentToString(), + encoded.array().contentToString() + ) + + verify(exactly = 1) { indexSector.decodeGroupNameHashes(maxGroupId, count, true, groupIds, buffer) } + verify(exactly = 1) { indexSector.encodeGroupNameHashes(count, true, groupIds, decoded) } + confirmVerified(indexSector) + } + + @Test + fun `test revisions`() { + val count = Random.nextInt(10..50) + val groupIds = IntStream.generate { Random.nextInt(count..count * 2) }.distinct().limit(count.toLong()).sorted().toArray() + val maxGroupId = (groupIds.maxOrNull() ?: 0) + 1 + + val buffer = ByteBuffer.wrap(Random.nextBytes(count * Int.SIZE_BYTES)) + + val indexSector = mockk() + every { indexSector.decodeGroupRevisions(any(), any(), any(), any()) } answers { callOriginal() } + every { indexSector.encodeGroupRevisions(any(), any(), any()) } answers { callOriginal() } + + val decoded = indexSector.decodeGroupRevisions(maxGroupId, count, groupIds, buffer) + val encoded = indexSector.encodeGroupRevisions(count, groupIds, decoded) + + assertEquals( + buffer.array().contentToString(), + encoded.array().contentToString() + ) + + verify(exactly = 1) { indexSector.decodeGroupRevisions(maxGroupId, count, groupIds, buffer) } + verify(exactly = 1) { indexSector.encodeGroupRevisions(count, groupIds, decoded) } + confirmVerified(indexSector) + } +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f371643..e750102 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/loader/build.gradle.kts b/loader/build.gradle.kts index d6d9d86..9496935 100644 --- a/loader/build.gradle.kts +++ b/loader/build.gradle.kts @@ -32,7 +32,6 @@ publishing { } } - artifact(tasks["javadocJar"]) artifact(tasks["sourcesJar"]) } diff --git a/loader/src/main/kotlin/com/runetopic/loader/IEntryBuilder.kt b/loader/src/main/kotlin/com/runetopic/loader/IEntryBuilder.kt index 0504f5d..c5dfd30 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/IEntryBuilder.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/IEntryBuilder.kt @@ -9,4 +9,4 @@ import java.nio.ByteBuffer internal interface IEntryBuilder { fun build(store: Js5Store) fun read(buffer: ByteBuffer, type: T): T -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/IEntryProvider.kt b/loader/src/main/kotlin/com/runetopic/loader/IEntryProvider.kt index 7347f55..c74a38e 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/IEntryProvider.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/IEntryProvider.kt @@ -7,7 +7,7 @@ import com.runetopic.cache.store.Js5Store */ interface IEntryProvider { fun load(store: Js5Store) - fun lookup(id: Int) : T + fun lookup(id: Int): T fun size(): Int fun collect(): Set -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/IEntryType.kt b/loader/src/main/kotlin/com/runetopic/loader/IEntryType.kt index f15e787..334f913 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/IEntryType.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/IEntryType.kt @@ -5,4 +5,4 @@ package com.runetopic.loader */ interface IEntryType { fun getId(): Int -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/extension/ByteArray.kt b/loader/src/main/kotlin/com/runetopic/loader/extension/ByteArray.kt index d1f668b..174cdd2 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/extension/ByteArray.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/extension/ByteArray.kt @@ -5,4 +5,4 @@ import java.nio.ByteBuffer /** * @author Jordan Abraham */ -fun ByteArray.toByteBuffer(): ByteBuffer = ByteBuffer.wrap(this) \ No newline at end of file +fun ByteArray.toByteBuffer(): ByteBuffer = ByteBuffer.wrap(this) diff --git a/loader/src/main/kotlin/com/runetopic/loader/extension/ByteBuffer.kt b/loader/src/main/kotlin/com/runetopic/loader/extension/ByteBuffer.kt index 8095593..f964712 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/extension/ByteBuffer.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/extension/ByteBuffer.kt @@ -82,4 +82,4 @@ val cp1252Identifiers = charArrayOf( '\u0000', '\u017e', '\u0178' -) \ No newline at end of file +) diff --git a/loader/src/main/kotlin/com/runetopic/loader/extension/Int.kt b/loader/src/main/kotlin/com/runetopic/loader/extension/Int.kt index 3a025f0..a9b220d 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/extension/Int.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/extension/Int.kt @@ -5,4 +5,4 @@ package com.runetopic.loader.extension */ internal fun Int.toBoolean(): Boolean { return this == 1 -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/idk/IdentityKitEntryBuilder.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/idk/IdentityKitEntryBuilder.kt index 53a1905..f2fbe69 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/idk/IdentityKitEntryBuilder.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/idk/IdentityKitEntryBuilder.kt @@ -12,11 +12,10 @@ import java.nio.ByteBuffer * @author Tyler Telis * @email */ -internal class IdentityKitEntryBuilder: IEntryBuilder { +internal class IdentityKitEntryBuilder : IEntryBuilder { lateinit var identityKitTypes: Set - @OptIn(ExperimentalStdlibApi::class) override fun build(store: Js5Store) { identityKitTypes = buildSet { store.index(2).group(3).files().forEach { @@ -33,19 +32,17 @@ internal class IdentityKitEntryBuilder: IEntryBuilder { val size = buffer.readUnsignedByte() val models = IntArray(size) - (0 until size).forEach { - models[it] = buffer.readUnsignedShort() - } + repeat(size) { models[it] = buffer.readUnsignedShort() } type.models = models } 3 -> { - // This is no longer used in higher revisions. OSRS uses this + // This is no longer used in higher revisions. OSRS uses this } 40 -> { val size = buffer.readUnsignedByte() val colorsToFind = ShortArray(size) val colorsToReplace = ShortArray(size) - (0 until size).forEach { + repeat(size) { colorsToFind[it] = buffer.readUnsignedShort().toShort() colorsToReplace[it] = buffer.readUnsignedShort().toShort() } @@ -56,7 +53,7 @@ internal class IdentityKitEntryBuilder: IEntryBuilder { val size = buffer.readUnsignedByte() val texturesToFind = ShortArray(size) val texturesToReplace = ShortArray(size) - (0 until size).forEach { + repeat(size) { texturesToFind[it] = buffer.readUnsignedShort().toShort() texturesToReplace[it] = buffer.readUnsignedShort().toShort() } @@ -64,8 +61,8 @@ internal class IdentityKitEntryBuilder: IEntryBuilder { type.texturesToReplace = texturesToReplace } in 60..69 -> buffer.readUnsignedShort().let { type.chatHeadModels[opcode - 60] = it } - else -> throw Exception("Read unused opcode with id: ${opcode}.") + else -> throw Exception("Read unused opcode with id: $opcode.") } while (true) return type } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/idk/IdentityKitEntryProvider.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/idk/IdentityKitEntryProvider.kt index 9c3106f..76d8a9e 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/idk/IdentityKitEntryProvider.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/idk/IdentityKitEntryProvider.kt @@ -3,7 +3,6 @@ package com.runetopic.loader.index.config.idk import com.runetopic.cache.store.Js5Store import com.runetopic.loader.IEntryProvider - /** * @author Jordan Abraham */ @@ -15,4 +14,4 @@ class IdentityKitEntryProvider : IEntryProvider { override fun lookup(id: Int): IdentityKitEntryType = builder.identityKitTypes.elementAt(id) override fun size(): Int = builder.identityKitTypes.size override fun collect(): Set = builder.identityKitTypes -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/idk/IdentityKitEntryType.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/idk/IdentityKitEntryType.kt index 428b0d0..8561131 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/idk/IdentityKitEntryType.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/idk/IdentityKitEntryType.kt @@ -10,7 +10,7 @@ data class IdentityKitEntryType( var texturesToFind: ShortArray? = null, var texturesToReplace: ShortArray? = null, var chatHeadModels: IntArray = intArrayOf(-1, -1, -1, -1, -1) -): IEntryType { +) : IEntryType { override fun getId(): Int = id override fun equals(other: Any?): Boolean { if (this === other) return true @@ -54,4 +54,4 @@ data class IdentityKitEntryType( result = 31 * result + chatHeadModels.contentHashCode() return result } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/inv/InventoryEntryBuilder.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/inv/InventoryEntryBuilder.kt index fb4a1da..22a2ce0 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/inv/InventoryEntryBuilder.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/inv/InventoryEntryBuilder.kt @@ -11,10 +11,9 @@ import java.nio.ByteBuffer * @author Tyler Telis * @email */ -internal class InventoryEntryBuilder: IEntryBuilder { +internal class InventoryEntryBuilder : IEntryBuilder { lateinit var inventoryTypes: Set - @OptIn(ExperimentalStdlibApi::class) override fun build(store: Js5Store) { inventoryTypes = buildSet { store.index(2).group(5).files().forEach { @@ -27,8 +26,8 @@ internal class InventoryEntryBuilder: IEntryBuilder { do when (val opcode = buffer.readUnsignedByte()) { 0 -> break 2 -> type.size = buffer.readUnsignedShort() - else -> throw Exception("Read unused opcode with id: ${opcode}.") + else -> throw Exception("Read unused opcode with id: $opcode.") } while (true) return type } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/inv/InventoryEntryProvider.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/inv/InventoryEntryProvider.kt index 3c149a7..0a53fb6 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/inv/InventoryEntryProvider.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/inv/InventoryEntryProvider.kt @@ -3,7 +3,6 @@ package com.runetopic.loader.index.config.inv import com.runetopic.cache.store.Js5Store import com.runetopic.loader.IEntryProvider - /** * @author Jordan Abraham */ @@ -15,4 +14,4 @@ class InventoryEntryProvider : IEntryProvider { override fun lookup(id: Int): InventoryEntryType = builder.inventoryTypes.elementAt(id) override fun size(): Int = builder.inventoryTypes.size override fun collect(): Set = builder.inventoryTypes -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/inv/InventoryEntryType.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/inv/InventoryEntryType.kt index 730dcef..4c9764e 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/inv/InventoryEntryType.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/inv/InventoryEntryType.kt @@ -5,7 +5,6 @@ import com.runetopic.loader.IEntryType data class InventoryEntryType( private val id: Int = 0, var size: Int = 0 -): IEntryType { +) : IEntryType { override fun getId(): Int = id - -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/lighting/LightingEntryBuilder.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/lighting/LightingEntryBuilder.kt index b66e032..d082847 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/lighting/LightingEntryBuilder.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/lighting/LightingEntryBuilder.kt @@ -15,7 +15,6 @@ internal class LightingEntryBuilder : IEntryBuilder { lateinit var lightings: Set - @OptIn(ExperimentalStdlibApi::class) override fun build(store: Js5Store) { lightings = buildSet { store.index(2).group(31).files().forEach { @@ -28,11 +27,11 @@ internal class LightingEntryBuilder : IEntryBuilder { do when (val opcode = buffer.readUnsignedByte()) { 0 -> break 1 -> type.anInt961 = buffer.readUnsignedByte() - 2-> type.anInt957 = buffer.readUnsignedShort() + 2 -> type.anInt957 = buffer.readUnsignedShort() 3 -> type.anInt956 = buffer.readUnsignedShort() 4 -> type.anInt962 = buffer.short.toInt() - else -> throw Exception("Read unused opcode with id: ${opcode}.") + else -> throw Exception("Read unused opcode with id: $opcode.") } while (true) return type } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/lighting/LightingEntryProvider.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/lighting/LightingEntryProvider.kt index d16efdd..53363d7 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/lighting/LightingEntryProvider.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/lighting/LightingEntryProvider.kt @@ -3,7 +3,6 @@ package com.runetopic.loader.index.config.lighting import com.runetopic.cache.store.Js5Store import com.runetopic.loader.IEntryProvider - /** * @author Tyler Telis * @email @@ -16,4 +15,4 @@ class LightingEntryProvider : IEntryProvider { override fun lookup(id: Int): LightingEntryType = builder.lightings.elementAt(id) override fun size(): Int = builder.lightings.size override fun collect(): Set = builder.lightings -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/lighting/LightingEntryType.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/lighting/LightingEntryType.kt index fd047ed..4eb2a21 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/lighting/LightingEntryType.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/lighting/LightingEntryType.kt @@ -8,6 +8,6 @@ data class LightingEntryType( var anInt962: Int = 0, var anInt956: Int = 2048, var anInt957: Int = 2048, -): IEntryType { - override fun getId(): Int = id -} \ No newline at end of file +) : IEntryType { + override fun getId(): Int = id +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/mouseicon/MouseIconEntryBuilder.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/mouseicon/MouseIconEntryBuilder.kt index 2d90b44..5743110 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/mouseicon/MouseIconEntryBuilder.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/mouseicon/MouseIconEntryBuilder.kt @@ -11,10 +11,9 @@ import java.nio.ByteBuffer * @author Tyler Telis * @email */ -internal class MouseIconEntryBuilder: IEntryBuilder { +internal class MouseIconEntryBuilder : IEntryBuilder { lateinit var mouseIconTypes: Set - @OptIn(ExperimentalStdlibApi::class) override fun build(store: Js5Store) { mouseIconTypes = buildSet { store.index(2).group(33).files().forEach { @@ -31,8 +30,8 @@ internal class MouseIconEntryBuilder: IEntryBuilder { type.xCoord = buffer.readUnsignedByte() type.yCoord = buffer.readUnsignedByte() } - else -> throw Exception("Read unused opcode with id: ${opcode}.") + else -> throw Exception("Read unused opcode with id: $opcode.") } while (true) return type } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/mouseicon/MouseIconEntryProvider.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/mouseicon/MouseIconEntryProvider.kt index fdf23e5..ded6f66 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/mouseicon/MouseIconEntryProvider.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/mouseicon/MouseIconEntryProvider.kt @@ -3,7 +3,6 @@ package com.runetopic.loader.index.config.mouseicon import com.runetopic.cache.store.Js5Store import com.runetopic.loader.IEntryProvider - /** * @author Jordan Abraham */ @@ -15,4 +14,4 @@ class MouseIconEntryProvider : IEntryProvider { override fun lookup(id: Int): MouseIconEntryType = builder.mouseIconTypes.elementAt(id) override fun size(): Int = builder.mouseIconTypes.size override fun collect(): Set = builder.mouseIconTypes -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/mouseicon/MouseIconEntryType.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/mouseicon/MouseIconEntryType.kt index 3805377..92d502e 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/mouseicon/MouseIconEntryType.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/mouseicon/MouseIconEntryType.kt @@ -7,7 +7,6 @@ data class MouseIconEntryType( var xCoord: Int = 0, var yCoord: Int = 0, var spriteId: Int = 0 -): IEntryType { +) : IEntryType { override fun getId(): Int = id - -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/overlay/OverlayEntryBuilder.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/overlay/OverlayEntryBuilder.kt index 781ac3c..0325876 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/overlay/OverlayEntryBuilder.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/overlay/OverlayEntryBuilder.kt @@ -11,11 +11,10 @@ import java.nio.ByteBuffer /** * @author Jordan Abraham */ -class OverlayEntryBuilder: IEntryBuilder { +class OverlayEntryBuilder : IEntryBuilder { lateinit var overlays: Set - @OptIn(ExperimentalStdlibApi::class) override fun build(store: Js5Store) { overlays = buildSet { store.index(2).group(4).files().forEach { @@ -35,7 +34,7 @@ class OverlayEntryBuilder: IEntryBuilder { 5 -> type.occlude = false 7 -> type.secondaryColor = buffer.readUnsignedMedium() 8 -> { - //Some sort of client usage happens here. + // Some sort of client usage happens here. } 9 -> type.textureResolution = buffer.readUnsignedShort() shl 2 10 -> type.aBoolean397 = false @@ -44,8 +43,8 @@ class OverlayEntryBuilder: IEntryBuilder { 13 -> type.anInt392 = buffer.readUnsignedMedium() 14 -> type.anInt395 = buffer.readUnsignedByte() shl 2 16 -> type.anInt388 = buffer.readUnsignedByte() - else -> throw Exception("Read unused opcode with id: ${opcode}.") + else -> throw Exception("Read unused opcode with id: $opcode.") } while (true) return type } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/overlay/OverlayEntryProvider.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/overlay/OverlayEntryProvider.kt index 5bdaf94..982614d 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/overlay/OverlayEntryProvider.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/overlay/OverlayEntryProvider.kt @@ -6,7 +6,7 @@ import com.runetopic.loader.IEntryProvider /** * @author Jordan Abraham */ -class OverlayEntryProvider: IEntryProvider { +class OverlayEntryProvider : IEntryProvider { private val builder = OverlayEntryBuilder() @@ -14,4 +14,4 @@ class OverlayEntryProvider: IEntryProvider { override fun lookup(id: Int): OverlayEntryType = builder.overlays.elementAt(id) override fun size(): Int = builder.overlays.size override fun collect(): Set = builder.overlays -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/overlay/OverlayEntryType.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/overlay/OverlayEntryType.kt index e1ee46d..e6d4660 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/overlay/OverlayEntryType.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/overlay/OverlayEntryType.kt @@ -18,6 +18,6 @@ data class OverlayEntryType( var anInt392: Int = 1190717, var anInt395: Int = 64, var anInt388: Int = 127 -): IEntryType { +) : IEntryType { override fun getId(): Int = id -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/param/ParamEntryBuilder.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/param/ParamEntryBuilder.kt index d7a05aa..d0eda38 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/param/ParamEntryBuilder.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/param/ParamEntryBuilder.kt @@ -12,11 +12,10 @@ import java.nio.ByteBuffer * @author Tyler Telis * @email */ -internal class ParamEntryBuilder: IEntryBuilder { +internal class ParamEntryBuilder : IEntryBuilder { lateinit var paramTypes: Set - @OptIn(ExperimentalStdlibApi::class) override fun build(store: Js5Store) { paramTypes = buildSet { store.index(2).group(11).files().forEach { @@ -32,8 +31,8 @@ internal class ParamEntryBuilder: IEntryBuilder { 2 -> type.defaultInt = buffer.int 4 -> type.aBoolean1822 = false 5 -> type.defaultString = buffer.readString() - else -> throw Exception("Read unused opcode with id: ${opcode}.") + else -> throw Exception("Read unused opcode with id: $opcode.") } while (true) return type } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/param/ParamEntryProvider.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/param/ParamEntryProvider.kt index d28b196..933a44e 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/param/ParamEntryProvider.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/param/ParamEntryProvider.kt @@ -3,7 +3,6 @@ package com.runetopic.loader.index.config.param import com.runetopic.cache.store.Js5Store import com.runetopic.loader.IEntryProvider - /** * @author Jordan Abraham */ @@ -15,4 +14,4 @@ class ParamEntryProvider : IEntryProvider { override fun lookup(id: Int): ParamEntryType = builder.paramTypes.elementAt(id) override fun size(): Int = builder.paramTypes.size override fun collect(): Set = builder.paramTypes -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/param/ParamEntryType.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/param/ParamEntryType.kt index d4ab77d..2de786f 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/param/ParamEntryType.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/param/ParamEntryType.kt @@ -8,6 +8,6 @@ data class ParamEntryType( var aBoolean1822: Boolean = true, var defaultString: String = "", var defaultInt: Int = 0 -): IEntryType { +) : IEntryType { override fun getId(): Int = id -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/skybox/SkyBoxEntryBuilder.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/skybox/SkyBoxEntryBuilder.kt index 10e7d51..d378d3d 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/skybox/SkyBoxEntryBuilder.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/skybox/SkyBoxEntryBuilder.kt @@ -11,10 +11,9 @@ import java.nio.ByteBuffer * @author Tyler Telis * @email */ -internal class SkyBoxEntryBuilder: IEntryBuilder { +internal class SkyBoxEntryBuilder : IEntryBuilder { lateinit var skyBoxTypes: Set - @OptIn(ExperimentalStdlibApi::class) override fun build(store: Js5Store) { skyBoxTypes = buildSet { store.index(2).group(29).files().forEach { @@ -30,13 +29,11 @@ internal class SkyBoxEntryBuilder: IEntryBuilder { 2 -> { val size = buffer.readUnsignedByte() val sphereIds = IntArray(size) - (0 until size).forEach { - sphereIds[it] = buffer.readUnsignedShort() - } + repeat(size) { sphereIds[it] = buffer.readUnsignedShort() } } 3 -> type.anInt2392 = buffer.readUnsignedByte() - else -> throw Exception("Read unused opcode with id: ${opcode}.") + else -> throw Exception("Read unused opcode with id: $opcode.") } while (true) return type } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/skybox/SkyBoxEntryProvider.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/skybox/SkyBoxEntryProvider.kt index d93f916..1681ba8 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/skybox/SkyBoxEntryProvider.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/skybox/SkyBoxEntryProvider.kt @@ -3,7 +3,6 @@ package com.runetopic.loader.index.config.skybox import com.runetopic.cache.store.Js5Store import com.runetopic.loader.IEntryProvider - /** * @author Jordan Abraham */ @@ -15,4 +14,4 @@ class SkyBoxEntryProvider : IEntryProvider { override fun lookup(id: Int): SkyBoxEntryType = builder.skyBoxTypes.elementAt(id) override fun size(): Int = builder.skyBoxTypes.size override fun collect(): Set = builder.skyBoxTypes -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/skybox/SkyBoxEntryType.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/skybox/SkyBoxEntryType.kt index d2aaea1..fa5d5e4 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/skybox/SkyBoxEntryType.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/skybox/SkyBoxEntryType.kt @@ -7,7 +7,7 @@ data class SkyBoxEntryType( var anInt2392: Int = -1, var sphereIds: IntArray? = null, var textureId: Int = -1 -): IEntryType { +) : IEntryType { override fun getId(): Int = id override fun equals(other: Any?): Boolean { if (this === other) return true @@ -33,4 +33,4 @@ data class SkyBoxEntryType( result = 31 * result + textureId return result } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/struct/StructEntryBuilder.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/struct/StructEntryBuilder.kt index 22ef172..1e2cb50 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/struct/StructEntryBuilder.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/struct/StructEntryBuilder.kt @@ -9,11 +9,10 @@ import java.nio.ByteBuffer * @author Tyler Telis * @email */ -internal class StructEntryBuilder: IEntryBuilder { +internal class StructEntryBuilder : IEntryBuilder { lateinit var structTypes: Set - @OptIn(ExperimentalStdlibApi::class) override fun build(store: Js5Store) { structTypes = buildSet { store.index(2).group(26).files().forEach { @@ -27,13 +26,13 @@ internal class StructEntryBuilder: IEntryBuilder { 0 -> break 249 -> { val size = buffer.readUnsignedByte() - (0 until size).forEach { _ -> + repeat(size) { val string = buffer.readUnsignedByte().toBoolean() type.params[buffer.readUnsignedMedium()] = if (string) buffer.readString() else buffer.int } } - else -> throw Exception("Read unused opcode with id: ${opcode}.") + else -> throw Exception("Read unused opcode with id: $opcode.") } while (true) return type } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/struct/StructEntryProvider.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/struct/StructEntryProvider.kt index e1c413a..f4957ec 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/struct/StructEntryProvider.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/struct/StructEntryProvider.kt @@ -3,7 +3,6 @@ package com.runetopic.loader.index.config.struct import com.runetopic.cache.store.Js5Store import com.runetopic.loader.IEntryProvider - /** * @author Jordan Abraham */ @@ -15,4 +14,4 @@ class StructEntryProvider : IEntryProvider { override fun lookup(id: Int): StructEntryType = builder.structTypes.elementAt(id) override fun size(): Int = builder.structTypes.size override fun collect(): Set = builder.structTypes -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/struct/StructEntryType.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/struct/StructEntryType.kt index 0c8992c..03b7ecf 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/struct/StructEntryType.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/struct/StructEntryType.kt @@ -5,6 +5,6 @@ import com.runetopic.loader.IEntryType data class StructEntryType( private val id: Int = 0, var params: MutableMap = mutableMapOf() -): IEntryType { +) : IEntryType { override fun getId(): Int = id -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/underlay/UnderlayEntryBuilder.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/underlay/UnderlayEntryBuilder.kt index 53450e4..ae9b085 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/underlay/UnderlayEntryBuilder.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/underlay/UnderlayEntryBuilder.kt @@ -11,11 +11,10 @@ import java.nio.ByteBuffer /** * @author Jordan Abraham */ -class UnderlayEntryBuilder: IEntryBuilder { +class UnderlayEntryBuilder : IEntryBuilder { lateinit var underlays: Set - @OptIn(ExperimentalStdlibApi::class) override fun build(store: Js5Store) { underlays = buildSet { store.index(2).group(1).files().forEach { @@ -34,8 +33,8 @@ class UnderlayEntryBuilder: IEntryBuilder { 3 -> type.textureResolution = buffer.readUnsignedShort() shl 2 4 -> type.aBoolean2647 = false 5 -> type.aBoolean2648 = false - else -> throw Exception("Read unused opcode with id: ${opcode}.") - } while(true) + else -> throw Exception("Read unused opcode with id: $opcode.") + } while (true) return type } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/underlay/UnderlayEntryProvider.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/underlay/UnderlayEntryProvider.kt index dff9043..6aa1052 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/underlay/UnderlayEntryProvider.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/underlay/UnderlayEntryProvider.kt @@ -14,4 +14,4 @@ class UnderlayEntryProvider : IEntryProvider { override fun lookup(id: Int): UnderlayEntryType = builder.underlays.elementAt(id) override fun size(): Int = builder.underlays.size override fun collect(): Set = builder.underlays -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/config/underlay/UnderlayEntryType.kt b/loader/src/main/kotlin/com/runetopic/loader/index/config/underlay/UnderlayEntryType.kt index d61d1bb..51498a3 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/config/underlay/UnderlayEntryType.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/config/underlay/UnderlayEntryType.kt @@ -12,6 +12,6 @@ data class UnderlayEntryType( var textureResolution: Int = 512, var aBoolean2647: Boolean = true, var aBoolean2648: Boolean = true -): IEntryType { +) : IEntryType { override fun getId(): Int = id -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/loc/LocEntryBuilder.kt b/loader/src/main/kotlin/com/runetopic/loader/index/loc/LocEntryBuilder.kt index b01fb7b..abc79d0 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/loc/LocEntryBuilder.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/loc/LocEntryBuilder.kt @@ -13,10 +13,9 @@ internal class LocEntryBuilder : IEntryBuilder { lateinit var mapTypes: Set - @OptIn(ExperimentalStdlibApi::class) override fun build(store: Js5Store) { mapTypes = buildSet { - store.index(16).use { index -> + store.index(16).let { index -> (0 until index.expand()).forEach { add(read(index.group(it ushr 8).file(it and 0xFF).data.toByteBuffer(), LocEntryType(it))) } @@ -32,14 +31,13 @@ internal class LocEntryBuilder : IEntryBuilder { val types = ByteArray(size) val models = Array(size) { intArrayOf() } - (0 until size).forEach { + repeat(size) { types[it] = buffer.get() val count = buffer.readUnsignedByte() models[it] = IntArray(count) - (0 until count).forEach { it2 -> - models[it][it2] = buffer.readUnsignedShort() - } + repeat(count) { it2 -> models[it][it2] = buffer.readUnsignedShort() } } + if (opcode == 5) { skipReadModelIds(buffer) } @@ -71,7 +69,7 @@ internal class LocEntryBuilder : IEntryBuilder { val size = buffer.readUnsignedByte() val colorsToFind = ShortArray(size) val colorsToReplace = ShortArray(size) - (0 until size).forEach { + repeat(size) { colorsToFind[it] = buffer.readUnsignedShort().toShort() colorsToReplace[it] = buffer.readUnsignedShort().toShort() } @@ -82,7 +80,7 @@ internal class LocEntryBuilder : IEntryBuilder { val size = buffer.readUnsignedByte() val texturesToFind = ShortArray(size) val texturesToReplace = ShortArray(size) - (0 until size).forEach { + repeat(size) { texturesToFind[it] = buffer.readUnsignedShort().toShort() texturesToReplace[it] = buffer.readUnsignedShort().toShort() } @@ -91,12 +89,10 @@ internal class LocEntryBuilder : IEntryBuilder { type.texturesToReplace = texturesToReplace } 42 -> { - val length = buffer.readUnsignedByte() - val aByteArray1118 = ByteArray(length) + val size = buffer.readUnsignedByte() + val aByteArray1118 = ByteArray(size) - (0 until length).forEach { - aByteArray1118[it] = buffer.get() - } + repeat(size) { aByteArray1118[it] = buffer.get() } type.aByteArray1118 = aByteArray1118 } 62 -> type.isRotated = true @@ -131,7 +127,7 @@ internal class LocEntryBuilder : IEntryBuilder { val size = buffer.readUnsignedByte() val configChangeDest = IntArray(size + 2) - (0..size).forEach { index -> + repeat(size + 1) { index -> buffer.readUnsignedShort().let { if (it == 65535) configChangeDest[index] = -1 else configChangeDest[index] = it @@ -149,12 +145,10 @@ internal class LocEntryBuilder : IEntryBuilder { type.anInt1145 = buffer.readUnsignedShort() type.anInt1139 = buffer.readUnsignedShort() type.anInt1144 = buffer.readUnsignedByte() - val length = buffer.readUnsignedByte() - val anIntArray1127 = IntArray(length) + val size = buffer.readUnsignedByte() + val anIntArray1127 = IntArray(size) - (0 until length).forEach { index -> - anIntArray1127[index] = buffer.readUnsignedShort() - } + repeat(size) { anIntArray1127[it] = buffer.readUnsignedShort() } type.anIntArray1127 = anIntArray1127 } @@ -193,27 +187,25 @@ internal class LocEntryBuilder : IEntryBuilder { 104 -> type.anInt1136 = buffer.readUnsignedByte() 105 -> type.aBoolean1148 = true 106 -> { - val length = buffer.readUnsignedByte() - val anIntArray1170 = IntArray(length) - val anIntArray1154 = IntArray(length) - (0 until length).forEach { index -> - anIntArray1170[index] = buffer.readUnsignedShort() - val size = buffer.readUnsignedByte() - anIntArray1154[index] = size - type.anInt1116 += size + val size = buffer.readUnsignedByte() + val anIntArray1170 = IntArray(size) + val anIntArray1154 = IntArray(size) + repeat(size) { + anIntArray1170[it] = buffer.readUnsignedShort() + val i = buffer.readUnsignedByte() + anIntArray1154[it] = i + type.anInt1116 += i } type.anIntArray1170 = anIntArray1170 type.anIntArray1154 = anIntArray1154 } 107 -> type.anInt1101 = buffer.readUnsignedShort() - in 150..154 -> buffer.readString().let { type.actions[opcode -150] = it } + in 150..154 -> buffer.readString().let { type.actions[opcode - 150] = it } 160 -> { - val length = buffer.readUnsignedByte() - val anIntArray1153 = IntArray(length) - (0 until length).forEach { index -> - anIntArray1153[index] = buffer.readUnsignedShort() - } + val size = buffer.readUnsignedByte() + val anIntArray1153 = IntArray(size) + repeat(size) { anIntArray1153[it] = buffer.readUnsignedShort() } type.anIntArray1153 = anIntArray1153 } 162 -> { @@ -241,13 +233,13 @@ internal class LocEntryBuilder : IEntryBuilder { 177 -> type.aBoolean1167 = true 178 -> type.anInt1113 = buffer.readUnsignedByte() 249 -> { - val length = buffer.readUnsignedByte() - (0 until length).forEach { _ -> + val size = buffer.readUnsignedByte() + repeat(size) { val string = buffer.readUnsignedByte().toBoolean() type.params[buffer.readUnsignedMedium()] = if (string) buffer.readString() else buffer.int } } - else -> throw Exception("Read unused opcode with id: ${opcode}.") + else -> throw Exception("Read unused opcode with id: $opcode.") } while (true) return type } @@ -260,4 +252,4 @@ internal class LocEntryBuilder : IEntryBuilder { buffer.position(buffer.position() + (count * 2)) } } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/loc/LocEntryProvider.kt b/loader/src/main/kotlin/com/runetopic/loader/index/loc/LocEntryProvider.kt index 9bab71f..6b920ee 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/loc/LocEntryProvider.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/loc/LocEntryProvider.kt @@ -3,7 +3,6 @@ package com.runetopic.loader.index.loc import com.runetopic.cache.store.Js5Store import com.runetopic.loader.IEntryProvider - /** * @author Tyler Telis * @email @@ -16,4 +15,4 @@ class LocEntryProvider : IEntryProvider { override fun lookup(id: Int): LocEntryType = builder.mapTypes.elementAt(id) override fun size(): Int = builder.mapTypes.size override fun collect(): Set = builder.mapTypes -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/loc/LocEntryType.kt b/loader/src/main/kotlin/com/runetopic/loader/index/loc/LocEntryType.kt index 521e44f..f0bf911 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/loc/LocEntryType.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/loc/LocEntryType.kt @@ -7,7 +7,7 @@ import com.runetopic.loader.IEntryType * @email */ data class LocEntryType( - private val id: Int= 0, + private val id: Int = 0, var types: ByteArray? = null, var models: Array? = null, var name: String = "null", @@ -87,6 +87,6 @@ data class LocEntryType( var aBoolean1167: Boolean = false, var anInt1113: Int = 0, var params: MutableMap = mutableMapOf(), - ): IEntryType { +) : IEntryType { override fun getId(): Int = id -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/map/MapEntryBuilder.kt b/loader/src/main/kotlin/com/runetopic/loader/index/map/MapEntryBuilder.kt index 16d8365..14354a4 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/map/MapEntryBuilder.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/map/MapEntryBuilder.kt @@ -17,14 +17,13 @@ internal class MapEntryBuilder : IEntryBuilder { lateinit var mapTypes: Set - @OptIn(ExperimentalStdlibApi::class) override fun build(store: Js5Store) { mapTypes = buildSet { - store.index(5).use { + store.index(5).let { (0..Short.MAX_VALUE).forEach { regionId -> val regionX: Int = regionId shr 8 val regionY: Int = regionId and 0xFF - it.group("m${regionX}_${regionY}").data.let { data -> + it.group("m${regionX}_$regionY").data.let { data -> if (data.isEmpty()) return@forEach add(read(data.toByteBuffer(), MapEntryType(regionId, regionX, regionY))) } @@ -34,9 +33,9 @@ internal class MapEntryBuilder : IEntryBuilder { } override fun read(buffer: ByteBuffer, type: MapEntryType): MapEntryType { - (0 until MapEntryType.PLANES).forEach { plane -> - (0 until MapEntryType.MAP_SIZE).forEach { x -> - (0 until MapEntryType.MAP_SIZE).forEach { z -> + repeat(MapEntryType.PLANES) { plane -> + repeat(MapEntryType.MAP_SIZE) { x -> + repeat(MapEntryType.MAP_SIZE) { z -> val tile = MapEntryType.Tile() while (true) { @@ -140,7 +139,7 @@ internal class MapEntryBuilder : IEntryBuilder { val cameraAngles = arrayOfNulls>(4) (0 until 4).forEach { index -> val i = buffer.get().toInt() - //0 checks if the array exists. + // 0 checks if the array exists. if (i == 1) { val regionParamX = 104 val regionParamY = 104 @@ -159,4 +158,4 @@ internal class MapEntryBuilder : IEntryBuilder { } } } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/map/MapEntryProvider.kt b/loader/src/main/kotlin/com/runetopic/loader/index/map/MapEntryProvider.kt index e6fd1b1..f1c691c 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/map/MapEntryProvider.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/map/MapEntryProvider.kt @@ -3,7 +3,6 @@ package com.runetopic.loader.index.map import com.runetopic.cache.store.Js5Store import com.runetopic.loader.IEntryProvider - /** * @author Tyler Telis * @email @@ -16,4 +15,4 @@ class MapEntryProvider : IEntryProvider { override fun lookup(id: Int): MapEntryType = builder.mapTypes.elementAt(id) override fun size(): Int = builder.mapTypes.size override fun collect(): Set = builder.mapTypes -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/map/MapEntryType.kt b/loader/src/main/kotlin/com/runetopic/loader/index/map/MapEntryType.kt index 046fdd8..5b54358 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/map/MapEntryType.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/map/MapEntryType.kt @@ -93,4 +93,4 @@ data class MapEntryType( const val MAP_SIZE = 64 const val PLANES = 4 } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/map/MapLocationEntryBuilder.kt b/loader/src/main/kotlin/com/runetopic/loader/index/map/MapLocationEntryBuilder.kt index 24e0a3b..24a7dca 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/map/MapLocationEntryBuilder.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/map/MapLocationEntryBuilder.kt @@ -19,11 +19,11 @@ internal class MapLocationEntryBuilder : IEntryBuilder { @OptIn(ExperimentalStdlibApi::class) override fun build(store: Js5Store) { mapTypes = buildSet { - store.index(5).use { + store.index(5).let { (0..Short.MAX_VALUE).forEach { regionId -> val regionX: Int = regionId shr 8 val regionY: Int = regionId and 0xFF - it.group("l${regionX}_${regionY}").data.let { data -> + it.group("l${regionX}_$regionY").data.let { data -> if (data.isEmpty()) return@forEach add(read(data.toByteBuffer(), MapLocationEntryType(regionId, regionX, regionY))) } @@ -64,4 +64,4 @@ internal class MapLocationEntryBuilder : IEntryBuilder { return type } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/map/MapLocationEntryProvider.kt b/loader/src/main/kotlin/com/runetopic/loader/index/map/MapLocationEntryProvider.kt index 62157d9..696923d 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/map/MapLocationEntryProvider.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/map/MapLocationEntryProvider.kt @@ -3,7 +3,6 @@ package com.runetopic.loader.index.map import com.runetopic.cache.store.Js5Store import com.runetopic.loader.IEntryProvider - /** * @author Tyler Telis * @email @@ -16,4 +15,4 @@ class MapLocationEntryProvider : IEntryProvider { override fun lookup(id: Int): MapLocationEntryType = builder.mapTypes.elementAt(id) override fun size(): Int = builder.mapTypes.size override fun collect(): Set = builder.mapTypes -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/map/MapLocationEntryType.kt b/loader/src/main/kotlin/com/runetopic/loader/index/map/MapLocationEntryType.kt index a7524cf..2c2d081 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/map/MapLocationEntryType.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/map/MapLocationEntryType.kt @@ -11,7 +11,7 @@ data class MapLocationEntryType( private val regionX: Int, private val regionY: Int, val locations: ArrayList = arrayListOf() -): IEntryType { +) : IEntryType { override fun getId(): Int = regionId class MapLocation( @@ -22,4 +22,4 @@ data class MapLocationEntryType( private val localY: Int, private val height: Int ) -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/npc/NpcEntryBuilder.kt b/loader/src/main/kotlin/com/runetopic/loader/index/npc/NpcEntryBuilder.kt index 87c1d01..dc854b4 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/npc/NpcEntryBuilder.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/npc/NpcEntryBuilder.kt @@ -8,15 +8,14 @@ import java.nio.ByteBuffer /** * @author Jordan Abraham */ -class NpcEntryBuilder: IEntryBuilder { +class NpcEntryBuilder : IEntryBuilder { lateinit var npcs: Set - @OptIn(ExperimentalStdlibApi::class) override fun build(store: Js5Store) { npcs = buildSet { - store.index(18).use { index -> - (0 until index.expand()).forEach { + store.index(18).let { index -> + repeat(index.expand()) { add(read(index.group(it ushr 8).file(it and 0xFF).data.toByteBuffer(), NpcEntryType(it))) } } @@ -29,7 +28,7 @@ class NpcEntryBuilder: IEntryBuilder { 1 -> { val size = buffer.readUnsignedByte() val models = IntArray(size) - (0 until size).forEach { + repeat(size) { models[it] = buffer.readUnsignedShort() if (models[it] == 65535) { models[it] = -1 @@ -44,7 +43,7 @@ class NpcEntryBuilder: IEntryBuilder { val size = buffer.readUnsignedByte() val colorToFind = ShortArray(size) val colorToReplace = ShortArray(size) - (0 until size).forEach { + repeat(size) { colorToFind[it] = buffer.readUnsignedShort().toShort() colorToReplace[it] = buffer.readUnsignedShort().toShort() } @@ -55,7 +54,7 @@ class NpcEntryBuilder: IEntryBuilder { val size = buffer.readUnsignedByte() val textureToFind = ShortArray(size) val textureToReplace = ShortArray(size) - (0 until size).forEach { + repeat(size) { textureToFind[it] = buffer.readUnsignedShort().toShort() textureToReplace[it] = buffer.readUnsignedShort().toShort() } @@ -65,17 +64,13 @@ class NpcEntryBuilder: IEntryBuilder { 42 -> { val size = buffer.readUnsignedByte() val aByteArray866 = ByteArray(size) - (0 until size).forEach { - aByteArray866[it] = buffer.get() - } + repeat(size) { aByteArray866[it] = buffer.get() } type.aByteArray866 = aByteArray866 } 60 -> { val size = buffer.readUnsignedByte() val chatheadModels = IntArray(size) - (0 until size).forEach { - chatheadModels[it] = buffer.readUnsignedShort() - } + repeat(size) { chatheadModels[it] = buffer.readUnsignedShort() } type.chatheadModels = chatheadModels } 93 -> type.isMinimapVisible = false @@ -105,7 +100,7 @@ class NpcEntryBuilder: IEntryBuilder { } val size = buffer.readUnsignedByte() val configChangeDest = IntArray(size + 2) - (0..size).forEach { + repeat(size + 1) { configChangeDest[it] = buffer.readUnsignedShort() if (configChangeDest[it] == 65535) { configChangeDest[it] = -1 @@ -129,7 +124,7 @@ class NpcEntryBuilder: IEntryBuilder { 121 -> { val anIntArrayArray845 = arrayOfNulls(type.models!!.size) val size = buffer.readUnsignedByte() - (0 until size).forEach { + repeat(size) { val i_98_ = buffer.readUnsignedByte() val data = (IntArray(3).also { anIntArrayArray845[i_98_] = it }) data[0] = buffer.get().toInt() @@ -176,7 +171,7 @@ class NpcEntryBuilder: IEntryBuilder { 141 -> type.aBoolean875 = true 142 -> type.anInt846 = buffer.readUnsignedShort() 143 -> type.aBoolean869 = true - in 150..154 -> { buffer.readString().let { type.options[opcode -150] = it } } + in 150..154 -> { buffer.readString().let { type.options[opcode - 150] = it } } 155 -> { type.aByte821 = buffer.get() type.aByte824 = buffer.get() @@ -188,9 +183,7 @@ class NpcEntryBuilder: IEntryBuilder { 160 -> { val size = buffer.readUnsignedByte() val anIntArray867 = IntArray(size) - (0 until size).forEach { - anIntArray867[it] = buffer.readUnsignedShort() - } + repeat(size) { anIntArray867[it] = buffer.readUnsignedShort() } } 162 -> type.aBoolean809 = true 163 -> type.anInt864 = buffer.readUnsignedByte() @@ -201,8 +194,8 @@ class NpcEntryBuilder: IEntryBuilder { 165 -> type.anInt847 = buffer.readUnsignedByte() 168 -> type.anInt828 = buffer.readUnsignedByte() 249 -> { - val length = buffer.readUnsignedByte() - (0 until length).forEach { _ -> + val size = buffer.readUnsignedByte() + repeat(size) { val string = buffer.readUnsignedByte().toBoolean() type.params[buffer.readUnsignedMedium()] = if (string) buffer.readString() else buffer.int } @@ -210,4 +203,4 @@ class NpcEntryBuilder: IEntryBuilder { } while (true) return type } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/npc/NpcEntryProvider.kt b/loader/src/main/kotlin/com/runetopic/loader/index/npc/NpcEntryProvider.kt index 4ccf7ed..40e656a 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/npc/NpcEntryProvider.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/npc/NpcEntryProvider.kt @@ -6,7 +6,7 @@ import com.runetopic.loader.IEntryProvider /** * @author Jordan Abraham */ -class NpcEntryProvider: IEntryProvider { +class NpcEntryProvider : IEntryProvider { private val builder = NpcEntryBuilder() @@ -14,4 +14,4 @@ class NpcEntryProvider: IEntryProvider { override fun lookup(id: Int): NpcEntryType = builder.npcs.elementAt(id) override fun size(): Int = builder.npcs.size override fun collect(): Set = builder.npcs -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/npc/NpcEntryType.kt b/loader/src/main/kotlin/com/runetopic/loader/index/npc/NpcEntryType.kt index 35a21be..f40b3d8 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/npc/NpcEntryType.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/npc/NpcEntryType.kt @@ -70,6 +70,6 @@ data class NpcEntryType( var aByte843: Byte = 0, var aBoolean809: Boolean = false, var params: MutableMap = mutableMapOf(), -): IEntryType { +) : IEntryType { override fun getId(): Int = id -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/obj/ObjEntryBuilder.kt b/loader/src/main/kotlin/com/runetopic/loader/index/obj/ObjEntryBuilder.kt index 137f3f0..a35a65d 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/obj/ObjEntryBuilder.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/obj/ObjEntryBuilder.kt @@ -12,11 +12,10 @@ internal class ObjEntryBuilder : IEntryBuilder { lateinit var objs: Set - @OptIn(ExperimentalStdlibApi::class) override fun build(store: Js5Store) { objs = buildSet { - store.index(19).use { index -> - (0 until index.expand()).forEach { + store.index(19).let { index -> + repeat(index.expand()) { add(read(index.group(it ushr 8).file(it and 0xFF).data.toByteBuffer(), ObjEntryType(it))) } } @@ -50,7 +49,7 @@ internal class ObjEntryBuilder : IEntryBuilder { val size = buffer.readUnsignedByte() val colorToFind = ShortArray(size) val colorToReplace = ShortArray(size) - (0 until size).forEach { + repeat(size) { colorToFind[it] = buffer.readUnsignedShort().toShort() colorToReplace[it] = buffer.readUnsignedShort().toShort() } @@ -61,7 +60,7 @@ internal class ObjEntryBuilder : IEntryBuilder { val size = buffer.readUnsignedByte() val textureToFind = ShortArray(size) val textureToReplace = ShortArray(size) - (0 until size).forEach { + repeat(size) { textureToFind[it] = buffer.readUnsignedShort().toShort() textureToReplace[it] = buffer.readUnsignedShort().toShort() } @@ -71,7 +70,7 @@ internal class ObjEntryBuilder : IEntryBuilder { 42 -> { val size = buffer.readUnsignedByte() val aByteArray1858 = ByteArray(size) - (0 until size).forEach { aByteArray1858[it] = buffer.readUnsignedByte().toByte() } + repeat(size) { aByteArray1858[it] = buffer.readUnsignedByte().toByte() } type.aByteArray1858 = aByteArray1858 } 65 -> type.tradeable = true @@ -130,7 +129,7 @@ internal class ObjEntryBuilder : IEntryBuilder { 132 -> { val size = buffer.readUnsignedByte() val anIntArray1893 = IntArray(size) - (0 until size).forEach { anIntArray1893[it] = buffer.readUnsignedShort() } + repeat(size) { anIntArray1893[it] = buffer.readUnsignedShort() } type.anIntArray1893 = anIntArray1893 } 134 -> type.anInt1902 = buffer.readUnsignedByte() @@ -138,13 +137,13 @@ internal class ObjEntryBuilder : IEntryBuilder { 140 -> type.anInt1885 = buffer.readUnsignedShort() 249 -> { val size = buffer.readUnsignedByte() - (0 until size).forEach { _ -> + repeat(size) { val string = buffer.readUnsignedByte().toBoolean() type.params[buffer.readUnsignedMedium()] = if (string) buffer.readString() else buffer.int } } - else -> throw Exception("Read unused opcode with id: ${opcode}.") + else -> throw Exception("Read unused opcode with id: $opcode.") } while (true) return type } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/obj/ObjEntryProvider.kt b/loader/src/main/kotlin/com/runetopic/loader/index/obj/ObjEntryProvider.kt index 921eaad..862c2fa 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/obj/ObjEntryProvider.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/obj/ObjEntryProvider.kt @@ -14,4 +14,4 @@ class ObjEntryProvider : IEntryProvider { override fun lookup(id: Int): ObjEntryType = builder.objs.elementAt(id) override fun size(): Int = builder.objs.size override fun collect(): Set = builder.objs -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/obj/ObjEntryType.kt b/loader/src/main/kotlin/com/runetopic/loader/index/obj/ObjEntryType.kt index cf5d137..6e08e7e 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/obj/ObjEntryType.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/obj/ObjEntryType.kt @@ -233,4 +233,4 @@ data class ObjEntryType( result = 31 * result + params.hashCode() return result } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/particle/ParticleEntryBuilder.kt b/loader/src/main/kotlin/com/runetopic/loader/index/particle/ParticleEntryBuilder.kt index f849a2d..fcc7678 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/particle/ParticleEntryBuilder.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/particle/ParticleEntryBuilder.kt @@ -12,7 +12,6 @@ internal class ParticleEntryBuilder : IEntryBuilder { lateinit var particles: Set - @OptIn(ExperimentalStdlibApi::class) override fun build(store: Js5Store) { particles = buildSet { store.index(27).group(0).files().forEach { @@ -55,13 +54,13 @@ internal class ParticleEntryBuilder : IEntryBuilder { 9 -> { val size = buffer.readUnsignedByte() val localMagnets = IntArray(size) - (0 until size).forEach { localMagnets[it] = buffer.readUnsignedShort() } + repeat(size) { localMagnets[it] = buffer.readUnsignedShort() } type.localMagnets = localMagnets } 10 -> { val size = buffer.readUnsignedByte() val globalMagnets = IntArray(size) - (0 until size).forEach { globalMagnets[it] = buffer.readUnsignedShort() } + repeat(size) { globalMagnets[it] = buffer.readUnsignedShort() } type.globalMagnets = globalMagnets } 12 -> type.minLevel = buffer.get().toInt() @@ -85,7 +84,7 @@ internal class ParticleEntryBuilder : IEntryBuilder { 25 -> { val size = buffer.readUnsignedByte() val generalMagnets = IntArray(size) - (0 until size).forEach { generalMagnets[it] = buffer.readUnsignedShort() } + repeat(size) { generalMagnets[it] = buffer.readUnsignedShort() } type.generalMagnets = generalMagnets } 26 -> type.aBoolean3070 = false @@ -100,8 +99,8 @@ internal class ParticleEntryBuilder : IEntryBuilder { 32 -> type.aBoolean3048 = false 33 -> type.aBoolean3023 = true 34 -> type.aBoolean3069 = false - else -> throw Exception("Read unused opcode with id: ${opcode}.") + else -> throw Exception("Read unused opcode with id: $opcode.") } while (true) return type } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/particle/ParticleEntryProvider.kt b/loader/src/main/kotlin/com/runetopic/loader/index/particle/ParticleEntryProvider.kt index 5d74c6a..4f49b68 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/particle/ParticleEntryProvider.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/particle/ParticleEntryProvider.kt @@ -14,4 +14,4 @@ class ParticleEntryProvider : IEntryProvider { override fun lookup(id: Int): ParticleEntryType = builder.particles.elementAt(id) override fun size(): Int = builder.particles.size override fun collect(): Set = builder.particles -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/particle/ParticleEntryType.kt b/loader/src/main/kotlin/com/runetopic/loader/index/particle/ParticleEntryType.kt index 6493f65..0555254 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/particle/ParticleEntryType.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/particle/ParticleEntryType.kt @@ -160,4 +160,4 @@ data class ParticleEntryType( result = 31 * result + colorFadePct return result } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/spotanim/SpotAnimationEntryBuilder.kt b/loader/src/main/kotlin/com/runetopic/loader/index/spotanim/SpotAnimationEntryBuilder.kt index 241d175..4348d82 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/spotanim/SpotAnimationEntryBuilder.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/spotanim/SpotAnimationEntryBuilder.kt @@ -10,15 +10,14 @@ import java.nio.ByteBuffer /** * @author Jordan Abraham */ -internal class SpotAnimationEntryBuilder: IEntryBuilder { +internal class SpotAnimationEntryBuilder : IEntryBuilder { lateinit var spotAnimations: Set - @OptIn(ExperimentalStdlibApi::class) override fun build(store: Js5Store) { spotAnimations = buildSet { - store.index(21).use { index -> - (0 until index.expand()).forEach { + store.index(21).let { index -> + repeat(index.expand()) { add(read(index.group(it ushr 8).file(it and 0xFF).data.toByteBuffer(), SpotAnimationEntryType(it))) } } @@ -59,7 +58,7 @@ internal class SpotAnimationEntryBuilder: IEntryBuilder val size = buffer.readUnsignedByte() val colorToFind = ShortArray(size) val colorToReplace = ShortArray(size) - (0 until size).forEach { + repeat(size) { colorToFind[it] = (buffer.readUnsignedShort()).toShort() colorToReplace[it] = (buffer.readUnsignedShort()).toShort() } @@ -70,15 +69,15 @@ internal class SpotAnimationEntryBuilder: IEntryBuilder val size = buffer.readUnsignedByte() val textureToFind = ShortArray(size) val textureToReplace = ShortArray(size) - (0 until size).forEach { + repeat(size) { textureToFind[it] = (buffer.readUnsignedShort()).toShort() textureToReplace[it] = (buffer.readUnsignedShort()).toShort() } type.textureToFind = textureToFind type.textureToReplace = textureToReplace } - else -> throw Exception("Read unused opcode with id: ${opcode}.") + else -> throw Exception("Read unused opcode with id: $opcode.") } while (true) return type } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/spotanim/SpotAnimationEntryProvider.kt b/loader/src/main/kotlin/com/runetopic/loader/index/spotanim/SpotAnimationEntryProvider.kt index 1036e18..8cf1dfa 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/spotanim/SpotAnimationEntryProvider.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/spotanim/SpotAnimationEntryProvider.kt @@ -3,7 +3,6 @@ package com.runetopic.loader.index.spotanim import com.runetopic.cache.store.Js5Store import com.runetopic.loader.IEntryProvider - /** * @author Jordan Abraham */ @@ -15,4 +14,4 @@ class SpotAnimationEntryProvider : IEntryProvider { override fun lookup(id: Int): SpotAnimationEntryType = builder.spotAnimations.elementAt(id) override fun size(): Int = builder.spotAnimations.size override fun collect(): Set = builder.spotAnimations -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/index/spotanim/SpotAnimationEntryType.kt b/loader/src/main/kotlin/com/runetopic/loader/index/spotanim/SpotAnimationEntryType.kt index c7dcf95..64c64a7 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/index/spotanim/SpotAnimationEntryType.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/index/spotanim/SpotAnimationEntryType.kt @@ -80,4 +80,4 @@ data class SpotAnimationEntryType( result = 31 * result + aBoolean2678.hashCode() return result } -} \ No newline at end of file +} diff --git a/loader/src/main/kotlin/com/runetopic/loader/util/vector/Vector3f.kt b/loader/src/main/kotlin/com/runetopic/loader/util/vector/Vector3f.kt index b52d5c1..b49017f 100644 --- a/loader/src/main/kotlin/com/runetopic/loader/util/vector/Vector3f.kt +++ b/loader/src/main/kotlin/com/runetopic/loader/util/vector/Vector3f.kt @@ -4,4 +4,4 @@ data class Vector3f( val x: Float, val y: Float, val z: Float -) \ No newline at end of file +) diff --git a/settings.gradle.kts b/settings.gradle.kts index 351b5c6..0484c32 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,7 +2,7 @@ rootProject.name = "cache-lib" pluginManagement { plugins { - kotlin("jvm") version "1.5.21" + kotlin("jvm") version "1.6.10" } } @@ -15,7 +15,6 @@ dependencyResolutionManagement { } } - include("app") include("cache") include("loader")