diff --git a/cache/build.gradle.kts b/cache/build.gradle.kts index 2866cb3a56..738d9433f8 100644 --- a/cache/build.gradle.kts +++ b/cache/build.gradle.kts @@ -4,6 +4,8 @@ dependencies { implementation("com.displee:rs-cache-library:${findProperty("displeeCacheVersion")}") implementation("io.insert-koin:koin-core:${findProperty("koinVersion")}") + implementation("com.github.jponge:lzma-java:1.3") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${findProperty("kotlinCoroutinesVersion")}") implementation("it.unimi.dsi:fastutil:${findProperty("fastUtilVersion")}") implementation("ch.qos.logback:logback-classic:${findProperty("logbackVersion")}") implementation("com.michael-bull.kotlin-inline-logger:kotlin-inline-logger-jvm:${findProperty("inlineLoggingVersion")}") diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/Cache.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/Cache.kt index 7772f6de4d..5e78bbbb45 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/Cache.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/Cache.kt @@ -2,25 +2,29 @@ package world.gregs.voidps.cache interface Cache { - fun getFile(index: Int, archive: Int, file: Int = 0, xtea: IntArray? = null): ByteArray? + fun indexCount(): Int - fun getFile(index: Int, name: String, xtea: IntArray? = null): ByteArray? + fun indices(): IntArray - fun close() + fun archives(index: Int): IntArray - fun getIndexCrc(indexId: Int): Int + fun archiveCount(index: Int): Int - fun archiveCount(indexId: Int, archiveId: Int): Int + fun lastArchiveId(indexId: Int): Int - fun lastFileId(indexId: Int, archive: Int): Int + fun archiveId(index: Int, hash: Int): Int - fun lastArchiveId(indexId: Int): Int + fun archiveId(index: Int, name: String): Int = archiveId(index, name.hashCode()) - fun getArchiveId(index: Int, name: String): Int + fun files(index: Int, archive: Int): IntArray - fun getArchiveId(index: Int, archive: Int): Int + fun fileCount(indexId: Int, archiveId: Int): Int - fun getArchives(index: Int): IntArray + fun lastFileId(indexId: Int, archive: Int): Int + + fun data(index: Int, archive: Int, file: Int = 0, xtea: IntArray? = null): ByteArray? + + fun data(index: Int, name: String, xtea: IntArray? = null) = data(index, archiveId(index, name), xtea = xtea) fun write(index: Int, archive: Int, file: Int, data: ByteArray, xteas: IntArray? = null) @@ -28,6 +32,6 @@ interface Cache { fun update(): Boolean - fun getArchiveData(index: Int, archive: Int): Map? + fun close() } \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/CacheDelegate.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/CacheDelegate.kt index 4357c0f12a..17313fdc27 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/CacheDelegate.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/CacheDelegate.kt @@ -2,53 +2,31 @@ package world.gregs.voidps.cache import com.displee.cache.CacheLibrary import com.github.michaelbull.logging.InlineLogger -import java.lang.ref.Reference -import java.lang.ref.SoftReference class CacheDelegate(directory: String) : Cache { - private val delegate: Reference - - private val logger = InlineLogger() + private val library: CacheLibrary init { val start = System.currentTimeMillis() - delegate = SoftReference(CacheLibrary(directory)) + library = CacheLibrary(directory) logger.info { "Cache read from $directory in ${System.currentTimeMillis() - start}ms" } } - override fun getFile(index: Int, archive: Int, file: Int, xtea: IntArray?) = - delegate.get()?.data(index, archive, file, xtea) - - override fun getFile(index: Int, name: String, xtea: IntArray?) = delegate.get()?.data(index, name, xtea) - - override fun close() { - delegate.get()?.close() - delegate.clear() - } + override fun indexCount() = library.indices().size - override fun getIndexCrc(indexId: Int): Int { - return delegate.get()?.index(indexId)?.crc ?: 0 - } + override fun indices() = library.indices().map { it.id }.toIntArray() - override fun archiveCount(indexId: Int, archiveId: Int): Int { - return delegate.get()?.index(indexId)?.archive(archiveId)?.fileIds()?.size ?: 0 - } + override fun archives(index: Int) = library.index(index).archiveIds() - override fun lastFileId(indexId: Int, archive: Int): Int { - return delegate.get()?.index(indexId)?.archive(archive)?.last()?.id ?: -1 - } + override fun archiveCount(index: Int) = library.index(index).archiveIds().size - override fun lastArchiveId(indexId: Int): Int { - return delegate.get()?.index(indexId)?.last()?.id ?: 0 - } + override fun lastArchiveId(indexId: Int) = library.index(indexId).last()?.id ?: -1 - override fun getArchiveId(index: Int, name: String): Int { - return delegate.get()?.index(index)?.archiveId(name) ?: -1 - } + override fun archiveId(index: Int, name: String) = library.index(index).archiveId(name) - override fun getArchiveId(index: Int, hash: Int): Int { - delegate.get()?.index(index)?.archives()?.forEach { archive -> + override fun archiveId(index: Int, hash: Int): Int { + for (archive in library.index(index).archives()) { if (archive.hashName == hash) { return archive.id } @@ -56,24 +34,34 @@ class CacheDelegate(directory: String) : Cache { return -1 } - override fun getArchiveData(index: Int, archive: Int): Map? { - return delegate.get()?.index(index)?.archive(archive)?.files?.mapValues { it.value?.data } - } + override fun files(index: Int, archive: Int) = library.index(index).archive(archive)?.fileIds() ?: IntArray(0) - override fun getArchives(index: Int): IntArray { - return delegate.get()?.index(index)?.archiveIds() ?: intArrayOf() - } + override fun fileCount(indexId: Int, archiveId: Int) = library.index(indexId).archive(archiveId)?.fileIds()?.size ?: 0 + + override fun lastFileId(indexId: Int, archive: Int) = library.index(indexId).archive(archive)?.last()?.id ?: -1 + + override fun data(index: Int, archive: Int, file: Int, xtea: IntArray?) = library.data(index, archive, file, xtea) + + override fun data(index: Int, name: String, xtea: IntArray?) = library.data(index, name, xtea) override fun write(index: Int, archive: Int, file: Int, data: ByteArray, xteas: IntArray?) { - delegate.get()?.put(index, archive, file, data, xteas) + library.put(index, archive, file, data, xteas) } override fun write(index: Int, archive: String, data: ByteArray, xteas: IntArray?) { - delegate.get()?.put(index, archive, data, xteas) + library.put(index, archive, data, xteas) } override fun update(): Boolean { - delegate.get()?.update() + library.update() return true } + + override fun close() { + library.close() + } + + companion object { + private val logger = InlineLogger() + } } \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/CacheLoader.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/CacheLoader.kt new file mode 100644 index 0000000000..9d306688ed --- /dev/null +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/CacheLoader.kt @@ -0,0 +1,25 @@ +package world.gregs.voidps.cache + +import java.io.File +import java.io.FileNotFoundException +import java.io.RandomAccessFile + +interface CacheLoader { + + fun load(path: String, xteas: Map? = null, threadUsage: Double = 1.0): Cache { + val mainFile = File(path, "${FileCache.CACHE_FILE_NAME}.dat2") + if (!mainFile.exists()) { + throw FileNotFoundException("Main file not found at '${mainFile.absolutePath}'.") + } + val main = RandomAccessFile(mainFile, "r") + val index255File = File(path, "${FileCache.CACHE_FILE_NAME}.idx255") + if (!index255File.exists()) { + throw FileNotFoundException("Checksum file not found at '${index255File.absolutePath}'.") + } + val index255 = RandomAccessFile(index255File, "r") + val indexCount = index255.length().toInt() / ReadOnlyCache.INDEX_SIZE + return load(path, mainFile, main, index255File, index255, indexCount, xteas, threadUsage) + } + + fun load(path: String, mainFile: File, main: RandomAccessFile, index255File: File, index255: RandomAccessFile, indexCount: Int, xteas: Map? = null, threadUsage: Double = 1.0): Cache +} \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/DefinitionDecoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/DefinitionDecoder.kt index 50b04d3d93..6d548f4550 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/DefinitionDecoder.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/DefinitionDecoder.kt @@ -3,35 +3,12 @@ package world.gregs.voidps.cache import com.github.michaelbull.logging.InlineLogger import world.gregs.voidps.buffer.read.BufferReader import world.gregs.voidps.buffer.read.Reader -import world.gregs.voidps.cache.active.ActiveCache -import java.io.File import java.nio.BufferUnderflowException abstract class DefinitionDecoder(val index: Int) { abstract fun create(size: Int): Array - /** - * Load from active cache - */ - fun load(cache: File): Array { - val start = System.currentTimeMillis() - val file = cache.resolve(fileName()) - if (!file.exists()) { - return create(0) - } - val reader = BufferReader(file.readBytes()) - val size = reader.readInt() + 1 - val array = create(size) - while (reader.position() < reader.length) { - load(array, reader) - } - logger.info { "$size ${this::class.simpleName} definitions loaded in ${System.currentTimeMillis() - start}ms" } - return array - } - - open fun fileName() = ActiveCache.indexFile(index) - open fun load(definitions: Array, reader: Reader) { val id = readId(reader) read(definitions, id, reader) @@ -42,7 +19,7 @@ abstract class DefinitionDecoder(val index: Int) { /** * Load from cache */ - open fun loadCache(cache: Cache): Array { + open fun load(cache: Cache): Array { val start = System.currentTimeMillis() val size = size(cache) + 1 val definitions = create(size) @@ -59,13 +36,13 @@ abstract class DefinitionDecoder(val index: Int) { } open fun size(cache: Cache): Int { - return cache.lastArchiveId(index) * 256 + (cache.archiveCount(index, cache.lastArchiveId(index))) + return cache.lastArchiveId(index) * 256 + (cache.fileCount(index, cache.lastArchiveId(index))) } open fun load(definitions: Array, cache: Cache, id: Int) { val archive = getArchive(id) val file = getFile(id) - val data = cache.getFile(index, archive, file) ?: return + val data = cache.data(index, archive, file) ?: return read(definitions, id, BufferReader(data)) } diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/FileCache.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/FileCache.kt new file mode 100644 index 0000000000..90a6d0bf3a --- /dev/null +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/FileCache.kt @@ -0,0 +1,79 @@ +package world.gregs.voidps.cache + +import world.gregs.voidps.cache.compress.DecompressionContext +import java.io.File +import java.io.RandomAccessFile + +/** + * [Cache] which reads data directly from file + * Average read speeds, fast loading and low but variable memory usage. + */ +class FileCache( + private val main: RandomAccessFile, + private val indexes: Array, + indexCount: Int, + val xteas: Map? +) : ReadOnlyCache(indexCount) { + + private val dataCache = object : LinkedHashMap>(16, 0.75f, true) { + override fun removeEldestEntry(eldest: MutableMap.MutableEntry>?): Boolean { + return size > 12 + } + } + private val length = main.length() + private val context = DecompressionContext() + + override fun data(index: Int, archive: Int, file: Int, xtea: IntArray?): ByteArray? { + val matchingIndex = files.getOrNull(index)?.getOrNull(archive)?.indexOf(file) ?: -1 + if (matchingIndex == -1) { + return null + } + val hash = index + (archive shl 6) + val files = dataCache.getOrPut(hash) { + val indexRaf = indexes[index] ?: return null + readFileData(context, main, length, indexRaf, index, archive, xteas) ?: return null + } + return files[matchingIndex] + } + + override fun close() { + main.close() + for (file in indexes) { + file?.close() + } + } + + companion object : CacheLoader { + const val CACHE_FILE_NAME = "main_file_cache" + + operator fun invoke(path: String, xteas: Map? = null): Cache { + return load(path, xteas) + } + + /** + * Create [RandomAccessFile]'s for each index file, load only the archive data into memory + */ + override fun load(path: String, mainFile: File, main: RandomAccessFile, index255File: File, index255: RandomAccessFile, indexCount: Int, xteas: Map?, threadUsage: Double): Cache { + val length = mainFile.length() + val context = DecompressionContext() + val indices = Array(indexCount) { indexId -> + val file = File(path, "${CACHE_FILE_NAME}.idx$indexId") + if (file.exists()) RandomAccessFile(file, "r") else null + } + val cache = FileCache(main, indices, indexCount, xteas) + for (indexId in 0 until indexCount) { + cache.readArchiveData(context, main, length, index255, indexId) + } + return cache + } + + @JvmStatic + fun main(args: Array) { + val path = "./data/cache/" + + val start = System.currentTimeMillis() + val cache = load(path) + println("Loaded cache in ${System.currentTimeMillis() - start}ms") + } + } +} \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/MemoryCache.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/MemoryCache.kt new file mode 100644 index 0000000000..054d325cf6 --- /dev/null +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/MemoryCache.kt @@ -0,0 +1,138 @@ +package world.gregs.voidps.cache + +import com.github.michaelbull.logging.InlineLogger +import kotlinx.coroutines.* +import world.gregs.voidps.cache.compress.DecompressionContext +import java.io.File +import java.io.RandomAccessFile + +/** + * [Cache] that holds all data in memory + * Read speeds are as fast, loading is slow and memory usage is high but stable. + * Loading is done in parallel as it is much slower to load than [FileCache] + */ +class MemoryCache(indexCount: Int) : ReadOnlyCache(indexCount) { + + val data: Array?>?> = arrayOfNulls(indexCount) + + override fun data(index: Int, archive: Int, file: Int, xtea: IntArray?): ByteArray? { + return data.getOrNull(index)?.getOrNull(archive)?.getOrNull(file) + } + + companion object : CacheLoader { + private val logger = InlineLogger() + + operator fun invoke(path: String, threadUsage: Double = 1.0, xteas: Map? = null): Cache { + return load(path, xteas, threadUsage) + } + + /** + * Load each index in parallel using a percentage of cpu cores + */ + @OptIn(DelicateCoroutinesApi::class) + override fun load(path: String, mainFile: File, main: RandomAccessFile, index255File: File, index255: RandomAccessFile, indexCount: Int, xteas: Map?, threadUsage: Double): Cache { + val cache = MemoryCache(indexCount) + val processors = (Runtime.getRuntime().availableProcessors() * threadUsage).toInt().coerceAtLeast(1) + newFixedThreadPoolContext(processors, "cache-loader").use { dispatcher -> + runBlocking(dispatcher) { + supervisorScope { + val fileLength = mainFile.length() + for (indexId in 0 until indexCount) { + launch { + loadIndex(path, indexId, mainFile, fileLength, index255File, xteas, processors, cache) + } + } + } + } + } + return cache + } + + /** + * Reads an indexes archive information before reading each archive in parallel + */ + private suspend fun loadIndex( + path: String, + indexId: Int, + mainFile: File, + mainFileLength: Long, + index255File: File, + xteas: Map?, + processors: Int, + cache: MemoryCache + ) { + val file = File(path, "${FileCache.CACHE_FILE_NAME}.idx$indexId") + if (!file.exists()) { + logger.trace { "No index $indexId file found." } + return + } + try { + val start = System.currentTimeMillis() + val main = withContext(Dispatchers.IO) { + RandomAccessFile(mainFile, "r") + } + val index255 = withContext(Dispatchers.IO) { + RandomAccessFile(index255File, "r") + } + val context = DecompressionContext() + val highest = cache.readArchiveData(context, main, mainFileLength, index255, indexId) + if (highest == -1) { + return + } + cache.data[indexId] = arrayOfNulls?>(highest + 1) + coroutineScope { + if (processors in 2 until highest) { + for (list in (0..highest).chunked(highest / processors)) { + launch { + loadArchives(cache, list, file, mainFile, mainFileLength, indexId, xteas) + } + } + } else { + loadArchives(cache, (0..highest).toList(), file, mainFile, mainFileLength, indexId, xteas) + } + } + logger.trace { "Loaded ${cache.data[indexId]!!.size} index $indexId archives in ${System.currentTimeMillis() - start}ms." } + } catch (e: Exception) { + logger.warn(e) { "Failed to load index $indexId." } + } + return + } + + /** + * Reads data for every file in each [archives] into the [cache] + */ + private fun loadArchives( + cache: MemoryCache, + archives: List, + file: File, + mainFile: File, + mainFileLength: Long, + indexId: Int, + xteas: Map? + ) { + val context = DecompressionContext() + val raf = RandomAccessFile(file, "r") + val main = RandomAccessFile(mainFile, "r") + for (archiveId in archives) { + val archiveFiles = cache.readFileData(context, main, mainFileLength, raf, indexId, archiveId, xteas) ?: continue + val archiveFileIds = cache.files[indexId]?.get(archiveId) ?: continue + val fileId = archiveFileIds.last() + val fileCount = cache.fileCounts[indexId]?.getOrNull(archiveId) ?: continue + val archiveData: Array = arrayOfNulls(fileId + 1) + for (fileIndex in 0 until fileCount) { + val data = archiveFiles[fileIndex] + archiveData[archiveFileIds[fileIndex]] = data + } + cache.data[indexId]!![archiveId] = archiveData + } + } + + @JvmStatic + fun main(args: Array) { + val path = "./data/cache/" + var start = System.currentTimeMillis() + val cache = load(path, null) + println("Loaded cache in ${System.currentTimeMillis() - start}ms") + } + } +} \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/ReadOnlyCache.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/ReadOnlyCache.kt new file mode 100644 index 0000000000..a1d2aebbe4 --- /dev/null +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/ReadOnlyCache.kt @@ -0,0 +1,245 @@ +package world.gregs.voidps.cache + +import com.github.michaelbull.logging.InlineLogger +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap +import world.gregs.voidps.buffer.read.BufferReader +import world.gregs.voidps.cache.compress.DecompressionContext +import java.io.RandomAccessFile + +/** + * [Cache] which efficiently stores information about its indexes, archives and files. + */ +abstract class ReadOnlyCache( + val indices: IntArray, + val archives: Array, + val fileCounts: Array, + val files: Array?>, + private val hashes: MutableMap +) : Cache { + + constructor(indexCount: Int) : this(IntArray(indexCount) { it }, arrayOfNulls(indexCount), arrayOfNulls(indexCount), arrayOfNulls(indexCount), Int2IntOpenHashMap(16384)) + + @Suppress("UNCHECKED_CAST") + internal fun readFileData( + context: DecompressionContext, + main: RandomAccessFile, + mainLength: Long, + indexRaf: RandomAccessFile, + indexId: Int, + archiveId: Int, + xteas: Map? + ): Array? { + val fileCounts = fileCounts[indexId] ?: return null + val fileIds = files[indexId] ?: return null + val fileCount = fileCounts.getOrNull(archiveId) ?: return null + val sectorData = readSector(main, mainLength, indexRaf, indexId, archiveId) ?: return null + val keys = if (xteas != null && indexId == Index.MAPS) xteas[archiveId] else null + val decompressed = context.decompress(sectorData, keys) ?: return null + + if (fileCount == 1) { + val fileId = fileIds[archiveId]?.last() ?: return null + return Array(fileId + 1) { + if (it == fileId) decompressed else null + } + } + + val reader = BufferReader(decompressed) + val rawArray = reader.array() + var fileDataSizesOffset = decompressed.size + val chunkSize: Int = rawArray[--fileDataSizesOffset].toInt() and 0xFF + fileDataSizesOffset -= chunkSize * (fileCount * 4) + val offsets = IntArray(fileCount) + reader.position(fileDataSizesOffset) + for (i in 0 until chunkSize) { + var previousLength = 0 + for (fileIndex in 0 until fileCount) { + previousLength += reader.readInt() + offsets[fileIndex] += previousLength + } + } + val archiveFiles = Array(fileCount) { index -> + val array = ByteArray(offsets[index]) + offsets[index] = 0 + array + } + var offset = 0 + reader.position(fileDataSizesOffset) + for (i in 0 until chunkSize) { + var length = 0 + for (fileIndex in 0 until fileCount) { + val read = reader.readInt() + val fileData = archiveFiles[fileIndex] + length += read + System.arraycopy(rawArray, offset, fileData, offsets[fileIndex], length) + offset += length + offsets[fileIndex] += length + } + } + return archiveFiles as Array + } + + internal fun readArchiveData( + context: DecompressionContext, + main: RandomAccessFile, + length: Long, + index255: RandomAccessFile, + indexId: Int + ): Int { + val archiveSector = readSector(main, length, index255, 255, indexId) + if (archiveSector == null) { + logger.trace { "Empty index $indexId." } + return -1 + } + val decompressed = context.decompress(archiveSector) ?: return -1 + val reader = BufferReader(decompressed) + val version = reader.readUnsignedByte() + if (version < 5 || version > 7) { + throw RuntimeException("Unknown version: $version") + } + if (version >= 6) { + reader.skip(4) // revision + } + val flags = reader.readByte() + val archiveCount = reader.readSmart(version) + var previous = 0 + var highest = 0 + val archiveIds = IntArray(archiveCount) { + val archiveId = reader.readSmart(version) + previous + previous = archiveId + if (archiveId > highest) { + highest = archiveId + } + archiveId + } + archives[indexId] = archiveIds + if (flags and NAME_FLAG != 0) { + for (i in 0 until archiveCount) { + val archiveId = archiveIds[i] + hashes[reader.readInt()] = archiveId + } + } + if (flags and WHIRLPOOL_FLAG != 0) { + reader.skip(archiveCount * WHIRLPOOL_SIZE) + } + reader.skip(archiveCount * 8) // Crc & revisions + val archiveSizes = IntArray(highest + 1) + for (i in 0 until archiveCount) { + val id = archiveIds[i] + val size = reader.readSmart(version) + archiveSizes[id] = size + } + fileCounts[indexId] = archiveSizes + val fileIds = arrayOfNulls(highest + 1) + files[indexId] = fileIds + for (i in 0 until archiveCount) { + var fileId = 0 + val archiveId = archiveIds[i] + val fileCount = archiveSizes[archiveId] + fileIds[archiveId] = IntArray(fileCount) { + fileId += reader.readSmart(version) + fileId + } + } + return highest + } + + override fun indexCount() = indices.size + + override fun indices() = indices + + override fun archives(index: Int) = archives.getOrNull(index) ?: IntArray(0) + + override fun archiveCount(index: Int) = archives.size + + override fun lastArchiveId(indexId: Int) = archives.getOrNull(indexId)?.last() ?: -1 + + override fun archiveId(index: Int, hash: Int) = hashes[hash] ?: -1 + + override fun files(index: Int, archive: Int) = files.getOrNull(index)?.getOrNull(archive) ?: IntArray(0) + + override fun fileCount(indexId: Int, archiveId: Int) = fileCounts.getOrNull(indexId)?.getOrNull(archiveId) ?: 0 + + override fun lastFileId(indexId: Int, archive: Int) = files.getOrNull(indexId)?.getOrNull(archive)?.last() ?: -1 + + override fun write(index: Int, archive: Int, file: Int, data: ByteArray, xteas: IntArray?) { + throw UnsupportedOperationException("Read only cache.") + } + + override fun write(index: Int, archive: String, data: ByteArray, xteas: IntArray?) { + throw UnsupportedOperationException("Read only cache.") + } + + override fun update(): Boolean { + return false + } + + override fun close() { + } + + companion object { + private val logger = InlineLogger() + private const val NAME_FLAG = 0x1 + private const val WHIRLPOOL_FLAG = 0x2 + + const val INDEX_SIZE = 6 + private const val WHIRLPOOL_SIZE = 64 + private const val SECTOR_SIZE = 520 + private const val SECTOR_HEADER_SIZE_SMALL = 8 + private const val SECTOR_DATA_SIZE_SMALL = 512 + private const val SECTOR_HEADER_SIZE_BIG = 10 + private const val SECTOR_DATA_SIZE_BIG = 510 + + private fun BufferReader.readSmart(version: Int) = if (version >= 7) readBigSmart() else readUnsignedShort() + + /** + * Reads a section of a cache's archive + */ + private fun readSector(mainFile: RandomAccessFile, length: Long, raf: RandomAccessFile, indexId: Int, sectorId: Int): ByteArray? { + if (length < INDEX_SIZE * sectorId + INDEX_SIZE) { + return null + } + raf.seek(sectorId.toLong() * INDEX_SIZE) + val sectorData = ByteArray(SECTOR_SIZE) + raf.read(sectorData, 0, INDEX_SIZE) + val bigSector = sectorId > 65535 + val buffer = BufferReader(sectorData) + val sectorSize = buffer.readUnsignedMedium() + var sectorPosition = buffer.readUnsignedMedium() + if (sectorSize < 0 || sectorPosition <= 0 || sectorPosition > mainFile.length() / SECTOR_SIZE) { + return null + } + var read = 0 + var chunk = 0 + val sectorHeaderSize = if (bigSector) SECTOR_HEADER_SIZE_BIG else SECTOR_HEADER_SIZE_SMALL + val sectorDataSize = if (bigSector) SECTOR_DATA_SIZE_BIG else SECTOR_DATA_SIZE_SMALL + val output = ByteArray(sectorSize) + while (read < sectorSize) { + if (sectorPosition == 0) { + return null + } + var requiredToRead = sectorSize - read + if (requiredToRead > sectorDataSize) { + requiredToRead = sectorDataSize + } + mainFile.seek(sectorPosition.toLong() * SECTOR_SIZE) + mainFile.read(sectorData, 0, requiredToRead + sectorHeaderSize) + buffer.position(0) + val id = if (bigSector) buffer.readInt() else buffer.readUnsignedShort() + val sectorChunk = buffer.readUnsignedShort() + val sectorNextPosition = buffer.readUnsignedMedium() + val sectorIndex = buffer.readUnsignedByte() + if (sectorIndex != indexId || id != sectorId || sectorChunk != chunk) { + return null + } else if (sectorNextPosition < 0 || sectorNextPosition > mainFile.length() / SECTOR_SIZE) { + return null + } + System.arraycopy(sectorData, sectorHeaderSize, output, read, requiredToRead) + read += requiredToRead + sectorPosition = sectorNextPosition + chunk++ + } + return output + } + } + +} \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/active/ActiveCache.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/active/ActiveCache.kt deleted file mode 100644 index 9e2d64521d..0000000000 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/active/ActiveCache.kt +++ /dev/null @@ -1,188 +0,0 @@ -package world.gregs.voidps.cache.active - -import com.github.michaelbull.logging.InlineLogger -import world.gregs.voidps.buffer.read.BufferReader -import world.gregs.voidps.buffer.write.BufferWriter -import world.gregs.voidps.cache.CacheDelegate -import world.gregs.voidps.cache.Config -import world.gregs.voidps.cache.Index -import world.gregs.voidps.cache.active.encode.* -import world.gregs.voidps.cache.secure.CRC -import java.io.File -import java.io.RandomAccessFile -import java.math.BigInteger -import java.security.MessageDigest - -/** - * Stores all the actively used data from the main cache into a small and fast to load format - */ -class ActiveCache( - private val encoders: () -> List = { load() } -) { - - private val logger = InlineLogger() - - /** - * Keeps the active cache up to date by checking for any cache modifications - */ - fun checkChanges(cachePath: String, activeDirectoryName: String) { - val cacheDir = File(cachePath) - if (!cacheDir.exists()) { - throw IllegalStateException("Unable to find cache.") - } - - val active = cacheDir.resolve(activeDirectoryName) - val checksum = active.resolve(CHECKSUM_FILE) - val mainFile = cacheDir.resolve("main_file_cache.dat2") - val index255 = cacheDir.resolve("main_file_cache.idx255") - - if (mainFile.exists() && index255.exists()) { - val encoders = encoders() - val crc = CRC(RandomAccessFile(mainFile.path, "r"), RandomAccessFile(index255.path, "r")) - if (checksum.exists()) { - val outdated = readChecksum(checksum, active, encoders, crc) - if (outdated > 0) { - update(cachePath, active, encoders, crc) - } - } else { - active.mkdir() - logger.info { "Creating active cache." } - update(cachePath, active, encoders, crc) - } - crc.close() - } else if (!checksum.exists()) { - throw IllegalStateException("Unable to find cache at '${cachePath}'.") - } - } - - /** - * Compares all values in [checksumFile] with current [activeDirectory] and cache [main], [index255] - * - */ - private fun readChecksum( - checksumFile: File, - activeDirectory: File, - indices: List, - crc32: CRC - ): Int { - val start = System.currentTimeMillis() - val reader = BufferReader(checksumFile.readBytes()) - if (reader.readByte() != VERSION) { - logger.info { "Checksum file out of date. Refreshing all." } - return indices.size - } - val size = reader.readByte() - var outdated = 0 - for (i in 0 until size) { - val index = reader.readByte() - val config = reader.readByte() - val crc = reader.readInt() - val md5 = reader.readString() - val encoder = indices.firstOrNull { it.index == index && it.config == config } - if (encoder == null) { - logger.warn { "No encoder found for index $index" } - continue - } - encoder.crc = crc32.read(index) - if (crc != encoder.crc) { - logger.debug { "CRC mismatch ${encoder.index}_${encoder.config} Expected: $crc Actual: ${encoder.crc}" } - outdated++ - continue - } - val file = encoder.file(activeDirectory) - if (!file.exists()) { - logger.debug { "Missing file ${encoder.index}_${encoder.config} ${file.path}" } - outdated++ - continue - } - encoder.md5 = md5(file.readBytes()) - if (md5 != encoder.md5) { - logger.debug { "MD5 mismatch ${encoder.index}_${encoder.config} Expected: $md5 Actual: ${encoder.md5}" } - outdated++ - continue - } - encoder.outdated = false - } - logger.info { "Found $outdated/$size outdated cache indices in ${System.currentTimeMillis() - start}ms" } - return outdated - } - - /** - * (Re)encodes any [Index] which are [ActiveIndexEncoder.outdated] - */ - private fun update(cachePath: String, active: File, encoders: List, crc32: CRC) { - val cache = CacheDelegate(cachePath) - val writer = BufferWriter(20_000_000) - for (encoder in encoders) { - val start = System.currentTimeMillis() - if (!encoder.outdated) { - continue - } - writer.clear() - encoder.encode(writer, cache) - if (writer.position() <= 0) { - continue - } - val file = encoder.file(active) - val bytes = writer.toArray() - file.writeBytes(bytes) - encoder.md5 = md5(bytes) - encoder.crc = crc32.read(encoder.index) - logger.info { "Encoded index ${encoder.index}_${encoder.config} in ${System.currentTimeMillis() - start}ms" } - } - writeChecksum(active.resolve(CHECKSUM_FILE), encoders) - } - - /** - * Write all the latest checksum values to file - */ - private fun writeChecksum(file: File, encoders: List) { - val writer = BufferWriter(encoders.sumOf { it.md5.length + 7 } + 2) - writer.writeByte(VERSION) - writer.writeByte(encoders.size) - for (encoder in encoders) { - writer.writeByte(encoder.index) - writer.writeByte(encoder.config) - writer.writeInt(encoder.crc) - writer.writeString(encoder.md5) - } - file.writeBytes(writer.array()) - } - - companion object { - - fun indexFile(index: Int) = "index$index.dat" - fun configFile(config: Int) = "config$config.dat" - - private const val CHECKSUM_FILE = "checksum.dat" - private const val VERSION = 3 - - private fun md5(bytes: ByteArray?): String { - val hash = MessageDigest.getInstance("MD5").digest(bytes) - return BigInteger(1, hash).toString(16) - } - - fun load(xteaPath: String = "./data/xteas.dat"): List { - return listOf( - ConfigEncoder(Config.IDENTITY_KIT), - ConfigEncoder(Config.INVENTORIES), - ConfigEncoder(Config.VARP), - ConfigEncoder(Config.VARC), - ConfigEncoder(Config.STRUCTS), - ConfigEncoder(Config.RENDER_ANIMATIONS), - InterfaceEncoder(), - MapEncoder(xteaPath), - HuffmanEncoder(), - ClientScriptEncoder(), - ShiftEncoder(Index.OBJECTS, 8), - ShiftEncoder(Index.ENUMS, 8), - ShiftEncoder(Index.NPCS, 7), - ShiftEncoder(Index.ITEMS, 8), - ShiftEncoder(Index.ANIMATIONS, 7), - ShiftEncoder(Index.GRAPHICS, 8), - ArchiveEncoder(Index.FONT_METRICS), - QuickChatEncoder(), - ) - } - } -} \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/active/ActiveIndexEncoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/active/ActiveIndexEncoder.kt deleted file mode 100644 index f84c92511b..0000000000 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/active/ActiveIndexEncoder.kt +++ /dev/null @@ -1,34 +0,0 @@ -package world.gregs.voidps.cache.active - -import world.gregs.voidps.buffer.write.Writer -import world.gregs.voidps.cache.Cache -import java.io.File - -open class ActiveIndexEncoder(val index: Int, val config: Int = 0) { - var crc: Int = -1 - var md5: String = "" - var outdated: Boolean = true - - open fun size(cache: Cache): Int { - return cache.lastArchiveId(index) * 256 + (cache.archiveCount(index, cache.lastArchiveId(index))) - } - - open fun file(directory: File) = directory.resolve(ActiveCache.indexFile(index)) - - open fun encode(writer: Writer, cache: Cache) { - writer.writeInt(size(cache)) - for (archiveId in cache.getArchives(index)) { - val files = cache.getArchiveData(index, archiveId) ?: continue - for ((fileId, data) in files) { - if (data == null) { - continue - } - encode(writer, index, archiveId, fileId, data) - } - } - } - - open fun encode(writer: Writer, index: Int, archive: Int, file: Int, data: ByteArray) { - writer.writeBytes(data) - } -} \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/ArchiveEncoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/ArchiveEncoder.kt deleted file mode 100644 index 6a64489090..0000000000 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/ArchiveEncoder.kt +++ /dev/null @@ -1,17 +0,0 @@ -package world.gregs.voidps.cache.active.encode - -import world.gregs.voidps.buffer.write.Writer -import world.gregs.voidps.cache.Cache -import world.gregs.voidps.cache.active.ActiveIndexEncoder - -class ArchiveEncoder(index: Int) : ActiveIndexEncoder(index) { - - override fun size(cache: Cache): Int { - return cache.lastArchiveId(index) - } - - override fun encode(writer: Writer, index: Int, archive: Int, file: Int, data: ByteArray) { - writer.writeInt(archive) - super.encode(writer, index, archive, file, data) - } -} \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/ClientScriptEncoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/ClientScriptEncoder.kt deleted file mode 100644 index 28c8495fe1..0000000000 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/ClientScriptEncoder.kt +++ /dev/null @@ -1,21 +0,0 @@ -package world.gregs.voidps.cache.active.encode - -import world.gregs.voidps.buffer.write.Writer -import world.gregs.voidps.cache.Cache -import world.gregs.voidps.cache.Index -import world.gregs.voidps.cache.active.ActiveIndexEncoder - -class ClientScriptEncoder : ActiveIndexEncoder(Index.CLIENT_SCRIPTS) { - - override fun size(cache: Cache): Int { - return cache.lastArchiveId(index) - } - - override fun encode(writer: Writer, index: Int, archive: Int, file: Int, data: ByteArray) { - if (archive == 1142) { // style script - writer.writeShort(archive) - writer.writeInt(data.size) - super.encode(writer, index, archive, file, data) - } - } -} \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/ConfigEncoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/ConfigEncoder.kt deleted file mode 100644 index 7db11511c6..0000000000 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/ConfigEncoder.kt +++ /dev/null @@ -1,26 +0,0 @@ -package world.gregs.voidps.cache.active.encode - -import world.gregs.voidps.buffer.write.Writer -import world.gregs.voidps.cache.Cache -import world.gregs.voidps.cache.Index -import world.gregs.voidps.cache.active.ActiveCache -import world.gregs.voidps.cache.active.ActiveIndexEncoder -import java.io.File - -class ConfigEncoder(config: Int) : ActiveIndexEncoder(Index.CONFIGS, config) { - - override fun size(cache: Cache): Int { - return cache.lastFileId(Index.CONFIGS, config) - } - - override fun file(directory: File): File { - return directory.resolve(ActiveCache.configFile(config)) - } - - override fun encode(writer: Writer, index: Int, archive: Int, file: Int, data: ByteArray) { - if (archive == config) { - writer.writeShort(file) - super.encode(writer, index, archive, file, data) - } - } -} \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/HuffmanEncoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/HuffmanEncoder.kt deleted file mode 100644 index a7d839d5be..0000000000 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/HuffmanEncoder.kt +++ /dev/null @@ -1,13 +0,0 @@ -package world.gregs.voidps.cache.active.encode - -import world.gregs.voidps.buffer.write.Writer -import world.gregs.voidps.cache.Cache -import world.gregs.voidps.cache.Index -import world.gregs.voidps.cache.active.ActiveIndexEncoder - -class HuffmanEncoder : ActiveIndexEncoder(Index.HUFFMAN) { - override fun encode(writer: Writer, cache: Cache) { - val data = cache.getFile(index, 1) ?: return - encode(writer, index, 1, 0, data) - } -} \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/InterfaceEncoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/InterfaceEncoder.kt deleted file mode 100644 index 9983aad5e5..0000000000 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/InterfaceEncoder.kt +++ /dev/null @@ -1,30 +0,0 @@ -package world.gregs.voidps.cache.active.encode - -import world.gregs.voidps.buffer.write.Writer -import world.gregs.voidps.cache.Cache -import world.gregs.voidps.cache.Index -import world.gregs.voidps.cache.active.ActiveIndexEncoder -import world.gregs.voidps.cache.definition.data.InterfaceDefinition - -class InterfaceEncoder : ActiveIndexEncoder(Index.INTERFACES) { - - override fun size(cache: Cache): Int { - return cache.lastArchiveId(index) - } - - override fun encode(writer: Writer, cache: Cache) { - writer.writeInt(size(cache)) - for (archiveId in cache.getArchives(index).reversed()) { - val files = cache.getArchiveData(index, archiveId) ?: continue - for (fileId in files.keys.reversed()) { - val data = files.getValue(fileId) ?: continue - encode(writer, index, archiveId, fileId, data) - } - } - } - - override fun encode(writer: Writer, index: Int, archive: Int, file: Int, data: ByteArray) { - writer.writeInt(InterfaceDefinition.pack(archive, file)) - super.encode(writer, index, archive, file, data) - } -} \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/MapEncoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/MapEncoder.kt deleted file mode 100644 index 00d5bc85e5..0000000000 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/MapEncoder.kt +++ /dev/null @@ -1,180 +0,0 @@ -package world.gregs.voidps.cache.active.encode - -import com.github.michaelbull.logging.InlineLogger -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap -import it.unimi.dsi.fastutil.ints.IntArrayList -import it.unimi.dsi.fastutil.ints.IntOpenHashSet -import world.gregs.voidps.buffer.read.BufferReader -import world.gregs.voidps.buffer.write.BufferWriter -import world.gregs.voidps.buffer.write.Writer -import world.gregs.voidps.cache.Cache -import world.gregs.voidps.cache.CacheDelegate -import world.gregs.voidps.cache.Index -import world.gregs.voidps.cache.active.ActiveIndexEncoder -import world.gregs.voidps.cache.definition.decoder.MapDecoder -import world.gregs.voidps.type.Region -import world.gregs.voidps.type.Zone -import java.io.File - -class MapEncoder( - private val xteasPath: String -) : ActiveIndexEncoder(Index.MAPS) { - - private val logger = InlineLogger() - private var empty = true - private var objectCount = 0 - private var tileCount = 0 - - override fun encode(writer: Writer, cache: Cache) { - objectCount = 0 - tileCount = 0 - val lastArchiveId = cache.lastArchiveId(Index.OBJECTS) - val objectSize = lastArchiveId * 256 + (cache.archiveCount(Index.OBJECTS, lastArchiveId)) - val definitions = MapDecoder(loadXteas(xteasPath)).loadCache(cache) - val start = System.currentTimeMillis() - val tiles = LongArray(TOTAL_ZONE_COUNT) - val objects = Int2ObjectOpenHashMap>() - val zones = IntOpenHashSet(85_000) - val full = IntOpenHashSet(18_000) - val all = IntOpenHashSet() - val levels = IntOpenHashSet() - var regions = 0 - for (definition in definitions) { - val region = Region(definition.id) - val regionZoneX = region.tile.zone.x - val regionZoneY = region.tile.zone.y - empty = true - for (level in 0 until 4) { - for (localX in 0 until 8) { - for (localY in 0 until 8) { - all.add(Zone.id(regionZoneX + localX, regionZoneY + localY, level)) - } - } - } - val emptyLevels = BooleanArray(4) { true } - for (level in 0 until 4) { - for (localX in 0 until 64) { - for (localY in 0 until 64) { - val blocked = definition.getTile(localX, localY, level).isTile(BLOCKED_TILE) - if (!blocked) { - continue - } - var height = level - val bridge = definition.getTile(localX, localY, 1).isTile(BRIDGE_TILE) - if (bridge) { - height-- - if (height < 0) { - continue - } - } - tileCount++ - empty = false - emptyLevels[height] = false - val zone = Zone.id(regionZoneX + (localX shr 3), regionZoneY + (localY shr 3), height) - val offset = (localX and 0x7) or ((localY and 0x7) shl 3) - tiles[zone] = tiles[zone] or (1L shl offset) - all.remove(zone) - if (tiles[zone] == -1L) { - full.add(zone) - zones.remove(zone) - } else { - zones.add(zone) - } - } - } - } - for (level in emptyLevels.indices) { - if (!emptyLevels[level]) { - continue - } - levels.add(region.toLevel(level).id) - for (x in 0 until 8) { - for (y in 0 until 8) { - all.remove(Zone.id(regionZoneX + x, regionZoneY + y, level)) - } - } - } - for (obj in definition.objects) { - val tile = region.tile.add(obj.x, obj.y) - objectCount++ - empty = false - if (obj.id > objectSize) { - logger.info { "Skipping $obj" } - continue - } - objects.getOrPut(tile.zone.id) { IntArrayList() }.add(ZoneObject.pack(obj.id, tile.x and 0x7, tile.y and 0x7, obj.level, obj.shape, obj.rotation)) - } - if (!empty) { - regions++ - } - } - writer.writeInt(regions) - writeEmptyTiles(writer, all, levels) - writeTiles(writer, zones, tiles) - writeFilledZones(writer, full) - writeObjects(writer, objects) - logger.info { "Compressed $regions maps ($objectCount objects, $tileCount tiles) to ${writer.position() / 1000000}mb in ${System.currentTimeMillis() - start}ms" } - } - - private fun writeEmptyTiles(writer: Writer, all: Set, levels: Set) { - writer.writeInt(levels.size) - for (level in levels) { - writer.writeInt(level) - } - writer.writeInt(all.size) - for (zone in all) { - writer.writeInt(zone) - } - } - - private fun writeTiles(writer: Writer, zones: Set, collisions: LongArray) { - writer.writeInt(zones.size) - for (zone in zones) { - writer.writeInt(zone) - writer.writeLong(collisions[zone]) - } - } - - private fun writeObjects(writer: Writer, objects: Map>) { - writer.writeInt(objects.size) - objects.forEach { (zone, objs) -> - writer.writeInt(zone) - writer.writeShort(objs.size) - for (obj in objs) { - writer.writeInt(obj) - } - } - } - - private fun writeFilledZones(writer: Writer, full: Set) { - writer.writeInt(full.size) - for (zone in full) { - writer.writeInt(zone) - } - } - - companion object { - private fun loadXteas(path: String): Map { - val xteas = Int2ObjectOpenHashMap() - val reader = BufferReader(File(path).readBytes()) - while (reader.position() < reader.length) { - val region = reader.readShort() - xteas[region] = IntArray(4) { reader.readInt() } - } - return xteas - } - - private const val TOTAL_ZONE_COUNT: Int = 2048 * 2048 * 4 - private const val BLOCKED_TILE = 0x1 - private const val BRIDGE_TILE = 0x2 - - @JvmStatic - fun main(args: Array) { - val cache = CacheDelegate("./data/cache") - val path = "./data/xteas.dat" - val writer = BufferWriter(20_000_000) - MapEncoder(path).encode(writer, cache) - File("./data/test-map-2.dat").writeBytes(writer.toArray()) - } - } -} \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/QuickChatEncoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/QuickChatEncoder.kt deleted file mode 100644 index a89c41e3a2..0000000000 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/QuickChatEncoder.kt +++ /dev/null @@ -1,21 +0,0 @@ -package world.gregs.voidps.cache.active.encode - -import world.gregs.voidps.buffer.write.Writer -import world.gregs.voidps.cache.Cache -import world.gregs.voidps.cache.Index -import world.gregs.voidps.cache.active.ActiveIndexEncoder - -class QuickChatEncoder : ActiveIndexEncoder(Index.QUICK_CHAT_MESSAGES) { - override fun encode(writer: Writer, index: Int, archive: Int, file: Int, data: ByteArray) { - if (archive == 1) { - writer.writeShort(file) - super.encode(writer, index, archive, file, data) - } - } - - override fun size(cache: Cache): Int { - val lastArchive = cache.lastArchiveId(index) - val lastArchive2 = cache.lastArchiveId(Index.QUICK_CHAT_MENUS) - return lastArchive * 256 + cache.lastFileId(index, lastArchive) + (lastArchive2 * 256 + cache.lastFileId(index, lastArchive2)) - } -} \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/ShiftEncoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/ShiftEncoder.kt deleted file mode 100644 index 68e11bfb2f..0000000000 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/ShiftEncoder.kt +++ /dev/null @@ -1,11 +0,0 @@ -package world.gregs.voidps.cache.active.encode - -import world.gregs.voidps.buffer.write.Writer -import world.gregs.voidps.cache.active.ActiveIndexEncoder - -class ShiftEncoder(index: Int, private val shift: Int) : ActiveIndexEncoder(index) { - override fun encode(writer: Writer, index: Int, archive: Int, file: Int, data: ByteArray) { - writer.writeInt(file or (archive shl shift)) - super.encode(writer, index, archive, file, data) - } -} \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/ZoneObject.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/ZoneObject.kt deleted file mode 100644 index f05c0dffad..0000000000 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/active/encode/ZoneObject.kt +++ /dev/null @@ -1,53 +0,0 @@ -package world.gregs.voidps.cache.active.encode - -@JvmInline -value class ZoneObject(val packed: Int) { - - constructor(id: Int, x: Int, y: Int, level: Int, shape: Int, rotation: Int) : this(pack(id, x, y, level, shape, rotation)) - - val id: Int - get() = id(packed) - val x: Int - get() = x(packed) - val y: Int - get() = y(packed) - val level: Int - get() = level(packed) - val shape: Int - get() = shape(packed) - val rotation: Int - get() = rotation(packed) - - override fun toString(): String { - return "ZoneObject(id=$id, x=$x, y=$y, level=$level, shape=$shape, rotation=$rotation)" - } - - companion object { - - fun pack(id: Int, x: Int, y: Int, level: Int, shape: Int, rotation: Int): Int { - return x + (y shl 3) + (level shl 6) + (rotation shl 8) + (shape shl 10) + (id shl 15) - } - - fun id(packed: Int): Int = packed shr 15 and 0x1ffff - fun x(packed: Int): Int = packed and 0x7 - fun y(packed: Int): Int = packed shr 3 and 0x7 - fun level(packed: Int): Int = packed shr 6 and 0x3 - fun shape(packed: Int): Int = packed shr 10 and 0x1f - fun rotation(packed: Int): Int = packed shr 8 and 0x3 - - /** - * Takes the first half of the [packed] value which is equivalent to Tile#index - */ - fun tile(value: Int): Int = value and 0x3f - - /** - * Takes the second half of [packed] which is the id, rotation and shape - * @see world.gregs.voidps.engine.entity.obj.GameObjects for usage - */ - fun info(value: Int): Int = value shr 8 - fun infoId(info: Int) = info shr 7 - fun infoRotation(info: Int) = info and 0x3 - fun infoShape(info: Int) = info shr 2 and 0x1f - - } -} \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/compress/BZIP2Compressor.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/compress/BZIP2Compressor.kt new file mode 100644 index 0000000000..47cdb88f1a --- /dev/null +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/compress/BZIP2Compressor.kt @@ -0,0 +1,515 @@ +package world.gregs.voidps.cache.compress + +/** + * A class representing the BZIP2 (de)compressor. + * @author Jagex + * @author Displee + */ +@Deprecated("Decompression is slow, use gzip for better performance. Kept for backwards compatability.") +internal class BZIP2Compressor { + private var anInt3070 = 0 + private var anInt3071 = 0 + private var aBooleanArray3072: BooleanArray = BooleanArray(16) + private var startOffset = 0 + private var anIntArray3075: IntArray = IntArray(256) + private var aByteArray3076: ByteArray = ByteArray(18002) + private var anInt3077 = 0 + private var anInt3078 = 0 + private var decompressed: ByteArray = ByteArray(0) + private var anInt3080 = 0 + private var anIntArrayArray3082 = Array(6) { IntArray(258) } + private var anInt3083 = 0 + private var anInt3085 = 0 + private var anInt3088 = 0 + private var anIntArray3090: IntArray = IntArray(6) + private var anIntArray3091: IntArray = IntArray(257) + private var anIntArray3092: IntArray = IntArray(16) + private var decompressedLength = 0 + private var aByteArray3094 = ByteArray(18002) + private var anIntArrayArray3095: Array = Array(6) { IntArray(258) } + private var anInt3097 = 0 + private var aByteArrayArray3098: Array = Array(6) { ByteArray(258) } + private var anIntArrayArray3099: Array = Array(6) { IntArray(258) } + private var anInt3100 = 0 + private var aByteArray3101: ByteArray = ByteArray(4096) + private var aBooleanArray3103: BooleanArray = BooleanArray(256) + private var anInt3106 = 0 + private var aByteArray3107: ByteArray = ByteArray(256) + private var aByte3108: Byte = 0 + private var compressed: ByteArray = ByteArray(0) + private var anIntArray5786: IntArray = IntArray(100000) + + /** + * Decompress a compressed BZIP2 file. + * @param decompressed An empty byte array where we put the decompressed data in. + * @param decompressedLength The length to decompress. + * @param archiveData The compressed BZIP2 file. + * @param startOffset The start offset. + * @return The decompressed length. + */ + @Deprecated("Use GZIP") + fun decompress(decompressed: ByteArray, decompressedLength: Int, archiveData: ByteArray, startOffset: Int): Int { + compressed = archiveData + this.startOffset = startOffset + this.decompressed = decompressed + anInt3100 = 0 + this.decompressedLength = decompressedLength + anInt3088 = 0 + anInt3078 = 0 + anInt3085 = 0 + anInt3097 = 0 + decompress() + compressed = byteArrayOf() + this.decompressed = byteArrayOf() + return decompressedLength - this.decompressedLength + } + + /** + * Decompress a BZIP2 block entry. + */ + private fun decompress() { + var i = 0 + var intArray1 = IntArray(0) + var intArray2 = IntArray(0) + var intArray3 = IntArray(0) + var bool_28_ = true + while (bool_28_) { + var i_29_ = method152(8) + if (i_29_ == 23) { + break + } + method152(8) + method152(8) + method152(8) + method152(8) + method152(8) + method152(8) + method152(8) + method152(8) + method152(8) + method152(1) + anInt3083 = 0 + var i_30_ = method152(8) + anInt3083 = anInt3083 shl 8 or (i_30_ and 0xff) + i_30_ = method152(8) + anInt3083 = anInt3083 shl 8 or (i_30_ and 0xff) + i_30_ = method152(8) + anInt3083 = anInt3083 shl 8 or (i_30_ and 0xff) + for (i_31_ in 0..15) { + i_29_ = method152(1) + if (i_29_ == 1) { + aBooleanArray3072[i_31_] = true + } else { + aBooleanArray3072[i_31_] = false + } + } + for (i_32_ in 0..255) { + aBooleanArray3103[i_32_] = false + } + for (i_33_ in 0..15) { + if (aBooleanArray3072[i_33_]) { + for (i_34_ in 0..15) { + i_29_ = method152(1) + if (i_29_ == 1) { + aBooleanArray3103[i_33_ * 16 + i_34_] = true + } + } + } + } + var anInt3073 = 0 + for (i in 0..255) { + if (aBooleanArray3103[i]) { + aByteArray3107[anInt3073] = i.toByte() + anInt3073++ + } + } + val i_35_ = anInt3073 + 2 + val i_36_ = method152(3) + val i_37_ = method152(15) + for (i_38_ in 0 until i_37_) { + var i_39_ = 0 + while (true) { + i_29_ = method152(1) + if (i_29_ == 0) { + break + } + i_39_++ + } + aByteArray3094[i_38_] = i_39_.toByte() + } + val is_40_ = ByteArray(6) + for (i_41_ in 0 until i_36_) { + is_40_[i_41_] = i_41_.toByte() + } + for (i_42_ in 0 until i_37_) { + var i_43_ = aByteArray3094[i_42_] + val i_44_ = is_40_[i_43_.toInt()] + while (i_43_ > 0) { + is_40_[i_43_.toInt()] = is_40_[i_43_ - 1] + i_43_-- + } + is_40_[0] = i_44_ + aByteArray3076[i_42_] = i_44_ + } + for (i_45_ in 0 until i_36_) { + var i_46_ = method152(5) + for (i_47_ in 0 until i_35_) { + while (true) { + i_29_ = method152(1) + if (i_29_ == 0) { + break + } + i_29_ = method152(1) + if (i_29_ == 0) { + i_46_++ + } else { + i_46_-- + } + } + aByteArrayArray3098[i_45_][i_47_] = i_46_.toByte() + } + } + for (i_48_ in 0 until i_36_) { + var i_49_ = 32 + var i_50_: Byte = 0 + for (i_51_ in 0 until i_35_) { + if (aByteArrayArray3098[i_48_][i_51_] > i_50_) { + i_50_ = aByteArrayArray3098[i_48_][i_51_] + } + if (aByteArrayArray3098[i_48_][i_51_] < i_49_) { + i_49_ = aByteArrayArray3098[i_48_][i_51_].toInt() + } + } + method145(anIntArrayArray3095[i_48_], + anIntArrayArray3082[i_48_], + anIntArrayArray3099[i_48_], + aByteArrayArray3098[i_48_], + i_49_, + i_50_.toInt(), + i_35_) + anIntArray3090[i_48_] = i_49_ + } + val i_52_ = anInt3073 + 1 + var i_53_ = -1 + var i_54_ = 0 + for (i_55_ in 0..255) { + anIntArray3075[i_55_] = 0 + } + var i_56_ = 4095 + for (i_57_ in 15 downTo 0) { + for (i_58_ in 15 downTo 0) { + aByteArray3101[i_56_] = (i_57_ * 16 + i_58_).toByte() + i_56_-- + } + anIntArray3092[i_57_] = i_56_ + 1 + } + var i_59_ = 0 + if (i_54_ == 0) { + i_53_++ + i_54_ = 50 + val i_60_ = aByteArray3076[i_53_] + i = anIntArray3090[i_60_.toInt()] + intArray1 = anIntArrayArray3095[i_60_.toInt()] + intArray3 = anIntArrayArray3099[i_60_.toInt()] + intArray2 = anIntArrayArray3082[i_60_.toInt()] + } + i_54_-- + var i_61_ = i + var i_62_: Int + var i_63_: Int + i_63_ = method152(i_61_) + while (i_63_ > intArray1[i_61_]) { + i_61_++ + i_62_ = method152(1) + i_63_ = i_63_ shl 1 or i_62_ + } + var i_64_ = intArray3[i_63_ - intArray2[i_61_]] + while (i_64_ != i_52_) { + if (i_64_ == 0 || i_64_ == 1) { + var i_65_ = -1 + var i_66_ = 1 + do { + if (i_64_ == 0) { + i_65_ += i_66_ + } else if (i_64_ == 1) { + i_65_ += 2 * i_66_ + } + i_66_ *= 2 + if (i_54_ == 0) { + i_53_++ + i_54_ = 50 + val i_67_ = aByteArray3076[i_53_] + i = anIntArray3090[i_67_.toInt()] + intArray1 = anIntArrayArray3095[i_67_.toInt()] + intArray3 = anIntArrayArray3099[i_67_.toInt()] + intArray2 = anIntArrayArray3082[i_67_.toInt()] + } + i_54_-- + i_61_ = i + i_63_ = method152(i_61_) + while (i_63_ > intArray1[i_61_]) { + i_61_++ + i_62_ = method152(1) + i_63_ = i_63_ shl 1 or i_62_ + } + i_64_ = intArray3[i_63_ - intArray2[i_61_]] + } while (i_64_ == 0 || i_64_ == 1) + i_65_++ + i_30_ = aByteArray3107[aByteArray3101[anIntArray3092[0]].toInt() and 0xff].toInt() + anIntArray3075[i_30_ and 0xff] += i_65_ + while ( /**/i_65_ > 0) { + anIntArray5786[i_59_] = i_30_ and 0xff + i_59_++ + i_65_-- + } + } else { + var i_68_ = i_64_ - 1 + if (i_68_ < 16) { + val i_69_ = anIntArray3092[0] + i_29_ = aByteArray3101[i_69_ + i_68_].toInt() + while ( /**/i_68_ > 3) { + val i_70_ = i_69_ + i_68_ + aByteArray3101[i_70_] = aByteArray3101[i_70_ - 1] + aByteArray3101[i_70_ - 1] = aByteArray3101[i_70_ - 2] + aByteArray3101[i_70_ - 2] = aByteArray3101[i_70_ - 3] + aByteArray3101[i_70_ - 3] = aByteArray3101[i_70_ - 4] + i_68_ -= 4 + } + while ( /**/i_68_ > 0) { + aByteArray3101[i_69_ + i_68_] = aByteArray3101[i_69_ + i_68_ - 1] + i_68_-- + } + aByteArray3101[i_69_] = i_29_.toByte() + } else { + var i_71_ = i_68_ / 16 + val i_72_ = i_68_ % 16 + var i_73_ = anIntArray3092[i_71_] + i_72_ + i_29_ = aByteArray3101[i_73_].toInt() + while (i_73_ > anIntArray3092[i_71_]) { + aByteArray3101[i_73_] = aByteArray3101[i_73_ - 1] + i_73_-- + } + anIntArray3092[i_71_]++ + while (i_71_ > 0) { + anIntArray3092[i_71_]-- + aByteArray3101[anIntArray3092[i_71_]] = aByteArray3101[anIntArray3092[i_71_ - 1] + 16 - 1] + i_71_-- + } + anIntArray3092[0]-- + aByteArray3101[anIntArray3092[0]] = i_29_.toByte() + if (anIntArray3092[0] == 0) { + var i_74_ = 4095 + for (i_75_ in 15 downTo 0) { + for (i_76_ in 15 downTo 0) { + aByteArray3101[i_74_] = aByteArray3101[anIntArray3092[i_75_] + i_76_] + i_74_-- + } + anIntArray3092[i_75_] = i_74_ + 1 + } + } + } + anIntArray3075[aByteArray3107[i_29_ and 0xff].toInt() and 0xff]++ + anIntArray5786[i_59_] = aByteArray3107[i_29_ and 0xff].toInt() and 0xff + i_59_++ + if (i_54_ == 0) { + i_53_++ + i_54_ = 50 + val i_77_ = aByteArray3076[i_53_] + i = anIntArray3090[i_77_.toInt()] + intArray1 = anIntArrayArray3095[i_77_.toInt()] + intArray3 = anIntArrayArray3099[i_77_.toInt()] + intArray2 = anIntArrayArray3082[i_77_.toInt()] + } + i_54_-- + i_61_ = i + i_63_ = method152(i_61_) + while (i_63_ > intArray1[i_61_]) { + i_61_++ + i_62_ = method152(1) + i_63_ = i_63_ shl 1 or i_62_ + } + i_64_ = intArray3[i_63_ - intArray2[i_61_]] + } + } + anInt3080 = 0 + aByte3108 = 0.toByte() + anIntArray3091[0] = 0 + for (i_78_ in 1..256) { + anIntArray3091[i_78_] = anIntArray3075[i_78_ - 1] + } + for (i_79_ in 1..256) { + anIntArray3091[i_79_] += anIntArray3091[i_79_ - 1] + } + for (i_80_ in 0 until i_59_) { + i_30_ = (anIntArray5786[i_80_] and 0xff).toByte().toInt() + anIntArray5786[anIntArray3091[i_30_ and 0xff]] = anIntArray5786[anIntArray3091[i_30_ and 0xff]] or (i_80_ shl 8) + anIntArray3091[i_30_ and 0xff]++ + } + anInt3106 = anIntArray5786[anInt3083] shr 8 + anInt3071 = 0 + anInt3106 = anIntArray5786[anInt3106] + anInt3070 = (anInt3106 and 0xff).toByte().toInt() + anInt3106 = anInt3106 shr 8 + anInt3071++ + anInt3077 = i_59_ + method151() + bool_28_ = anInt3071 == anInt3077 + 1 && anInt3080 == 0 + } + } + + private fun method152(arg0: Int): Int { + while (true) { + if (anInt3088 >= arg0) { + val i_93_ = anInt3078 shr anInt3088 - arg0 and (1 shl arg0) - 1 + anInt3088 -= arg0 + return i_93_ + } + anInt3078 = anInt3078 shl 8 or (compressed[startOffset].toInt() and 0xff) + anInt3088 += 8 + startOffset++ + anInt3085++ + } + } + + private fun method151() { + var i = aByte3108 + var i_81_ = anInt3080 + var i_82_ = anInt3071 + var i_83_ = anInt3070 + val data = anIntArray5786 + var i_84_ = anInt3106 + val is_85_ = decompressed + var i_86_ = anInt3100 + var i_87_ = decompressedLength + val i_88_ = i_87_ + val i_89_ = anInt3077 + 1 + while_68_@ while (true) { + if (i_81_ > 0) { + while (true) { + if (i_87_ == 0) { + break@while_68_ + } + if (i_81_ == 1) { + break + } + is_85_[i_86_] = i + i_81_-- + i_86_++ + i_87_-- + } + if (i_87_ == 0) { + i_81_ = 1 + break + } + is_85_[i_86_] = i + i_86_++ + i_87_-- + } + var bool = true + while (bool) { + bool = false + if (i_82_ == i_89_) { + i_81_ = 0 + break@while_68_ + } + i = i_83_.toByte() + i_84_ = data[i_84_] + val i_90_ = (i_84_ and 0xff).toByte().toInt() + i_84_ = i_84_ shr 8 + i_82_++ + if (i_90_ != i_83_) { + i_83_ = i_90_ + if (i_87_ == 0) { + i_81_ = 1 + break@while_68_ + } + is_85_[i_86_] = i + i_86_++ + i_87_-- + bool = true + } else if (i_82_ == i_89_) { + if (i_87_ == 0) { + i_81_ = 1 + break@while_68_ + } + is_85_[i_86_] = i + i_86_++ + i_87_-- + bool = true + } + } + i_81_ = 2 + i_84_ = data[i_84_] + var i_91_ = (i_84_ and 0xff).toByte().toInt() + i_84_ = i_84_ shr 8 + if (++i_82_ != i_89_) { + if (i_91_ != i_83_) { + i_83_ = i_91_ + } else { + i_81_ = 3 + i_84_ = data[i_84_] + i_91_ = (i_84_ and 0xff).toByte().toInt() + i_84_ = i_84_ shr 8 + if (++i_82_ != i_89_) { + if (i_91_ != i_83_) { + i_83_ = i_91_ + } else { + i_84_ = data[i_84_] + i_91_ = (i_84_ and 0xff).toByte().toInt() + i_84_ = i_84_ shr 8 + i_82_++ + i_81_ = (i_91_ and 0xff) + 4 + i_84_ = data[i_84_] + i_83_ = (i_84_ and 0xff).toByte().toInt() + i_84_ = i_84_ shr 8 + i_82_++ + } + } + } + } + } + anInt3097 += i_88_ - i_87_ + aByte3108 = i + anInt3080 = i_81_ + anInt3071 = i_82_ + anInt3070 = i_83_ + anIntArray5786 = data + anInt3106 = i_84_ + decompressed = is_85_ + anInt3100 = i_86_ + decompressedLength = i_87_ + } + + private fun method145(arg0: IntArray, arg1: IntArray, arg2: IntArray, arg3: ByteArray, arg4: Int, arg5: Int, arg6: Int) { + var i = 0 + for (i_0_ in arg4..arg5) { + for (i_1_ in 0 until arg6) { + if (arg3[i_1_].toInt() == i_0_) { + arg2[i] = i_1_ + i++ + } + } + } + for (i_2_ in 0..22) { + arg1[i_2_] = 0 + } + for (i_3_ in 0 until arg6) { + arg1[arg3[i_3_] + 1]++ + } + for (i_4_ in 1..22) { + arg1[i_4_] += arg1[i_4_ - 1] + } + for (i_5_ in 0..22) { + arg0[i_5_] = 0 + } + var i_6_ = 0 + for (i_7_ in arg4..arg5) { + i_6_ += arg1[i_7_ + 1] - arg1[i_7_] + arg0[i_7_] = i_6_ - 1 + i_6_ = i_6_ shl 1 + } + for (i_8_ in arg4 + 1..arg5) { + arg1[i_8_] = (arg0[i_8_ - 1] + 1 shl 1) - arg1[i_8_] + } + } +} \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/compress/DecompressionContext.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/compress/DecompressionContext.kt new file mode 100644 index 0000000000..2e0fa0a37f --- /dev/null +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/compress/DecompressionContext.kt @@ -0,0 +1,111 @@ +package world.gregs.voidps.cache.compress + +import com.github.michaelbull.logging.InlineLogger +import lzma.sdk.lzma.Decoder +import world.gregs.voidps.buffer.read.BufferReader +import world.gregs.voidps.cache.secure.Xtea +import java.io.ByteArrayInputStream +import java.io.OutputStream +import java.util.concurrent.atomic.AtomicBoolean +import java.util.zip.Inflater + +/** + * Context per thread for decompressing data in parallel + */ +internal class DecompressionContext { + private val gzipInflater = Inflater(true) + private val bzip2Compressor: BZIP2Compressor by lazy { BZIP2Compressor() } + private val lzmaDecoder: Decoder by lazy { Decoder() } + + fun decompress(data: ByteArray, keys: IntArray? = null): ByteArray? { + if (keys != null && (keys[0] != 0 || keys[1] != 0 || keys[2] != 0 || 0 != keys[3])) { + Xtea.decipher(data, keys, 5) + } + val buffer = BufferReader(data) + val type = buffer.readUnsignedByte() + val compressedSize = buffer.readInt() and 0xFFFFFF + var decompressedSize = 0 + if (type != 0) { + decompressedSize = buffer.readInt() and 0xFFFFFF + } + when (type) { + NONE -> { + val decompressed = ByteArray(compressedSize) + buffer.readBytes(decompressed, 0, compressedSize) + return decompressed + } + BZIP2 -> { + if (!warned.get()) { + logger.warn { "GZIP2 Compression found - replace to improve read performance." } + warned.set(true) + } + val decompressed = ByteArray(decompressedSize) + bzip2Compressor.decompress(decompressed, decompressedSize, data, 9) + return decompressed + } + GZIP -> { + val offset = buffer.position() + if (buffer.readByte() != 31 || buffer.readByte() != -117) { + return null + } + return try { + val decompressed = ByteArray(decompressedSize) + gzipInflater.setInput(data, offset + 10, data.size - (offset + 18)) + gzipInflater.finished() + gzipInflater.inflate(decompressed) + decompressed + } catch (exception: Exception) { + logger.warn(exception) { "Error decompressing gzip data." } + null + } finally { + gzipInflater.reset() + } + } + LZMA -> { + val decompressed = ByteArray(decompressedSize) + decompress(data, buffer.position(), decompressed, decompressedSize) + return decompressed + } + } + return null + } + + private fun decompress(compressed: ByteArray, offset: Int, decompressed: ByteArray, decompressedLength: Int) { + if (!lzmaDecoder.setDecoderProperties(compressed)) { + logger.error { "LZMA: Bad properties." } + return + } + val input = ByteArrayInputStream(compressed) + input.skip(offset.toLong()) + val output = ByteArrayWrapperOutputStream(decompressed) + lzmaDecoder.code(input, output, decompressedLength.toLong()) + } + + private class ByteArrayWrapperOutputStream(private val byteArray: ByteArray) : OutputStream() { + private var position = 0 + + override fun write(b: Int) { + byteArray[position++] = b.toByte() + } + + override fun write(b: ByteArray, off: Int, len: Int) { + System.arraycopy(b, off, byteArray, position, len) + position += len + } + + override fun flush() { + } + + override fun close() { + } + } + + companion object { + private const val NONE = 0 + private const val BZIP2 = 1 + private const val GZIP = 2 + private const val LZMA = 3 + private val warned = AtomicBoolean() + private val logger = InlineLogger() + } +} \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/config/ConfigDecoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/config/ConfigDecoder.kt index 4d081ee1c9..dee68fe779 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/config/ConfigDecoder.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/config/ConfigDecoder.kt @@ -5,14 +5,11 @@ import world.gregs.voidps.cache.Cache import world.gregs.voidps.cache.Definition import world.gregs.voidps.cache.DefinitionDecoder import world.gregs.voidps.cache.Index -import world.gregs.voidps.cache.active.ActiveCache abstract class ConfigDecoder(internal val archive: Int) : DefinitionDecoder(Index.CONFIGS) { override fun getArchive(id: Int) = archive - override fun fileName() = ActiveCache.configFile(archive) - override fun readId(reader: Reader): Int { return reader.readShort() } diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/InterfaceComponentDefinitionFull.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/InterfaceComponentDefinitionFull.kt index d9e9f571eb..60f446ce78 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/InterfaceComponentDefinitionFull.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/InterfaceComponentDefinitionFull.kt @@ -58,7 +58,7 @@ data class InterfaceComponentDefinitionFull( var viewportHeight: Int = 0, var lineWidth: Int = 1, var lineMirrored: Boolean = false, - var keyRepeat: ByteArray? = null, + var keyRepeats: ByteArray? = null, var keyCodes: ByteArray? = null, var keyModifiers: IntArray? = null, var clickable: Boolean = false, @@ -165,10 +165,10 @@ data class InterfaceComponentDefinitionFull( if (viewportHeight != other.viewportHeight) return false if (lineWidth != other.lineWidth) return false if (lineMirrored != other.lineMirrored) return false - if (keyRepeat != null) { - if (other.keyRepeat == null) return false - if (!keyRepeat.contentEquals(other.keyRepeat)) return false - } else if (other.keyRepeat != null) return false + if (keyRepeats != null) { + if (other.keyRepeats == null) return false + if (!keyRepeats.contentEquals(other.keyRepeats)) return false + } else if (other.keyRepeats != null) return false if (keyCodes != null) { if (other.keyCodes == null) return false if (!keyCodes.contentEquals(other.keyCodes)) return false @@ -363,7 +363,7 @@ data class InterfaceComponentDefinitionFull( result = 31 * result + viewportHeight result = 31 * result + lineWidth result = 31 * result + lineMirrored.hashCode() - result = 31 * result + (keyRepeat?.contentHashCode() ?: 0) + result = 31 * result + (keyRepeats?.contentHashCode() ?: 0) result = 31 * result + (keyCodes?.contentHashCode() ?: 0) result = 31 * result + (keyModifiers?.contentHashCode() ?: 0) result = 31 * result + clickable.hashCode() diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/MapDefinition.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/MapDefinition.kt index c14b05dc61..ce455289d7 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/MapDefinition.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/MapDefinition.kt @@ -15,7 +15,7 @@ class MapDefinition( } companion object { - internal fun index(localX: Int, localY: Int, level: Int): Int { + fun index(localX: Int, localY: Int, level: Int): Int { return (level shl 12) + (localX shl 6) + localY } internal fun localX(tile: Int) = tile shr 6 and 0x3f diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/ClientScriptDecoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/ClientScriptDecoder.kt index 2901ea25dc..3121652640 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/ClientScriptDecoder.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/ClientScriptDecoder.kt @@ -7,7 +7,7 @@ import world.gregs.voidps.cache.DefinitionDecoder import world.gregs.voidps.cache.Index import world.gregs.voidps.cache.definition.data.ClientScriptDefinition -class ClientScriptDecoder(private val revision634: Boolean) : DefinitionDecoder(Index.CLIENT_SCRIPTS) { +class ClientScriptDecoder(private val revision667: Boolean = false) : DefinitionDecoder(Index.CLIENT_SCRIPTS) { override fun size(cache: Cache): Int { return cache.lastArchiveId(index) @@ -38,17 +38,17 @@ class ClientScriptDecoder(private val revision634: Boolean) : DefinitionDecoder< override fun ClientScriptDefinition.read(opcode: Int, buffer: Reader) { buffer.position(buffer.length - 2) val i = buffer.readShort() - val length: Int = buffer.length - (2 + i) - if (revision634) 12 else 16 + val length: Int = buffer.length - 2 - i - (if (revision667) 16 else 12) buffer.position(length) val instructionCount = buffer.readInt() intVariableCount = buffer.readShort() stringVariableCount = buffer.readShort() - if (!revision634) { + if (revision667) { longVariableCount = buffer.readShort() } intArgumentCount = buffer.readShort() stringArgumentCount = buffer.readShort() - if (!revision634) { + if (revision667) { longArgumentCount = buffer.readShort() } val count = buffer.readUnsignedByte() @@ -75,7 +75,7 @@ class ClientScriptDecoder(private val revision634: Boolean) : DefinitionDecoder< stringOperands = arrayOfNulls(instructionCount) } stringOperands!![index] = buffer.readString().intern() - } else if (!revision634 && clientOpcode == 54) { + } else if (revision667 && clientOpcode == 54) { if (longOperands == null) { longOperands = LongArray(instructionCount) } @@ -84,7 +84,7 @@ class ClientScriptDecoder(private val revision634: Boolean) : DefinitionDecoder< if (intOperands == null) { intOperands = IntArray(instructionCount) } - if (clientOpcode < 100 && clientOpcode != 21 && clientOpcode != 38 && clientOpcode != 39) { + if (clientOpcode < 150 && clientOpcode != 21 && clientOpcode != 38 && clientOpcode != 39) { intOperands!![index] = buffer.readInt() } else { intOperands!![index] = buffer.readUnsignedByte() @@ -93,5 +93,4 @@ class ClientScriptDecoder(private val revision634: Boolean) : DefinitionDecoder< instructions[index++] = clientOpcode } } - } \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/InterfaceDecoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/InterfaceDecoder.kt index ea042ce1de..785d5344de 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/InterfaceDecoder.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/InterfaceDecoder.kt @@ -39,7 +39,7 @@ class InterfaceDecoder : DefinitionDecoder(INTERFACES) { val definition = definitions[id] val components = Int2ObjectOpenHashMap(2) for (i in 0..lastArchive) { - val data = cache.getFile(index, archiveId, i) + val data = cache.data(index, archiveId, i) if (data != null) { val componentDefinition = InterfaceComponentDefinition(id = InterfaceDefinition.pack(id, i)) if (!componentDefinition.isEmpty(BufferReader(data))) { diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/InterfaceDecoderFull.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/InterfaceDecoderFull.kt index 7593a9b174..c27d1b91ed 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/InterfaceDecoderFull.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/InterfaceDecoderFull.kt @@ -36,7 +36,7 @@ class InterfaceDecoderFull : DefinitionDecoder(INTERFAC val definition = definitions[id] val components = Array(lastArchive + 1) { InterfaceComponentDefinitionFull(id = it + (id shl 16)) } for (i in 0..lastArchive) { - val data = cache.getFile(index, archiveId, i) + val data = cache.data(index, archiveId, i) if (data != null) { components[i].read(BufferReader(data)) } @@ -44,7 +44,7 @@ class InterfaceDecoderFull : DefinitionDecoder(INTERFAC definition.components = components } - internal fun InterfaceComponentDefinitionFull.read(buffer: Reader) { + fun InterfaceComponentDefinitionFull.read(buffer: Reader) { buffer.readUnsignedByte() type = buffer.readUnsignedByte() if (type and 0x80 != 0) { @@ -143,7 +143,7 @@ class InterfaceDecoderFull : DefinitionDecoder(INTERFAC val setting = buffer.readUnsignedMedium() var i_21_ = buffer.readUnsignedByte() if (i_21_ != 0) { - keyRepeat = ByteArray(11) + keyRepeats = ByteArray(11) keyCodes = ByteArray(11) keyModifiers = IntArray(11) while (i_21_ != 0) { @@ -159,7 +159,7 @@ class InterfaceDecoderFull : DefinitionDecoder(INTERFAC } val b_24_ = buffer.readByte().toByte() keyModifiers!![i_22_] = i_21_ - keyRepeat!![i_22_] = b_23_ + keyRepeats!![i_22_] = b_23_ keyCodes!![i_22_] = b_24_ i_21_ = buffer.readUnsignedByte() } diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/MapDecoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/MapDecoder.kt index f006743ce9..048dbe8cad 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/MapDecoder.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/MapDecoder.kt @@ -7,14 +7,14 @@ import world.gregs.voidps.cache.Cache import world.gregs.voidps.cache.DefinitionDecoder import world.gregs.voidps.cache.Index.MAPS import world.gregs.voidps.cache.definition.data.MapDefinition -import world.gregs.voidps.cache.definition.data.MapObject -import world.gregs.voidps.cache.definition.data.MapTile import world.gregs.voidps.type.Region -class MapDecoder(private val xteas: Map) : DefinitionDecoder(MAPS) { +class MapDecoder(val xteas: Map? = null) : DefinitionDecoder(MAPS) { + + private val objects = MapObjectDefinitionDecoder(xteas) override fun MapDefinition.read(opcode: Int, buffer: Reader) { - TODO("Not yet implemented") + throw UnsupportedOperationException("Not in use.") } override fun create(size: Int) = Array(size) { MapDefinition(it) } @@ -33,119 +33,30 @@ class MapDecoder(private val xteas: Map) : DefinitionDecoder = Int2IntOpenHashMap(1600) - override fun loadCache(cache: Cache): Array { + override fun load(cache: Cache): Array { regionHashes.clear() for (regionX in 0 until 256) { for (regionY in 0 until 256) { - val archiveId = cache.getArchiveId(index, "m${regionX}_$regionY") + val archiveId = cache.archiveId(index, "m${regionX}_$regionY") if (archiveId == -1) { continue } regionHashes[archiveId] = Region.id(regionX, regionY) } } - return super.loadCache(cache) + return super.load(cache) } override fun load(definitions: Array, cache: Cache, id: Int) { val region = regionHashes[id] ?: return - val data = cache.getFile(index, id, 0, null) ?: return + val data = cache.data(index, id, 0, null) ?: return val reader = BufferReader(data) val definition = definitions[id] definition.id = region - readLoop(definition, reader) - loadObjects(cache, definition) + MapTileDecoder.loadTiles(reader, definition.tiles) + objects.decode(cache, definition) } override fun readLoop(definition: MapDefinition, buffer: Reader) { - for (level in 0 until 4) { - for (localX in 0 until 64) { - for (localY in 0 until 64) { - var height = 0 - var attrOpcode = 0 - var overlayPath = 0 - var overlayRotation = 0 - var overlayId = 0 - var settings = 0 - var underlayId = 0 - loop@ while (true) { - val config = buffer.readUnsignedByte() - if (config == 0) { - break@loop - } else if (config == 1) { - height = buffer.readUnsignedByte() - break@loop - } else if (config <= 49) { - attrOpcode = config - overlayId = buffer.readUnsignedByte() - overlayPath = (config - 2) / 4 - overlayRotation = 3 and (config - 2) - } else if (config <= 81) { - settings = config - 49 - } else { - underlayId = (config - 81) and 0xff - } - } - if (height != 0 || attrOpcode != 0 || overlayPath != 0 || overlayRotation != 0 || overlayId != 0 || settings != 0 || underlayId != 0) { - definition.setTile(localX, localY, level, MapTile( - height, - attrOpcode, - overlayId, - overlayPath, - overlayRotation, - settings, - underlayId - )) - } - } - } - } - } - - private fun loadObjects(cache: Cache, definition: MapDefinition) { - val objectData = cache.getFile(index, "l${definition.id shr 8}_${definition.id and 0xff}", xteas[definition.id]) ?: return - val reader = BufferReader(objectData) - var objectId = -1 - while (true) { - val skip = reader.readLargeSmart() - if (skip == 0) { - break - } - objectId += skip - var tile = 0 - while (true) { - val loc = reader.readSmart() - if (loc == 0) { - break - } - tile += loc - 1 - - // Data - val localX = tile shr 6 and 0x3f - val localY = tile and 0x3f - var level = tile shr 12 - val obj = reader.readUnsignedByte() - - // Decrease bridges - if (definition.getTile(localX, localY, 1).isTile(BRIDGE_TILE)) { - level-- - } - - // Validate level - if (level !in 0 until 4) { - continue - } - - val shape = obj shr 2 - val rotation = obj and 0x3 - - // Valid object - definition.objects.add(MapObject(objectId, localX, localY, level, shape, rotation)) - } - } - } - - companion object { - private const val BRIDGE_TILE = 0x2 } } \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/MapObjectDecoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/MapObjectDecoder.kt new file mode 100644 index 0000000000..340d49f6b3 --- /dev/null +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/MapObjectDecoder.kt @@ -0,0 +1,65 @@ +package world.gregs.voidps.cache.definition.decoder + +import world.gregs.voidps.buffer.read.BufferReader +import world.gregs.voidps.cache.definition.data.MapDefinition +import world.gregs.voidps.cache.definition.data.MapTile + +/** + * Decodes all objects in a map except bridges + */ +abstract class MapObjectDecoder { + + /** + * Decodes object information and calls [add] for each using [tiles] to skip bridge objects + */ + fun decode(reader: BufferReader, tiles: LongArray, regionTileX: Int, regionTileY: Int) { + var objectId = -1 + while (true) { + val skip = reader.readLargeSmart() + if (skip == 0) { + break + } + objectId += skip + var tile = 0 + while (true) { + val loc = reader.readSmart() + if (loc == 0) { + break + } + tile += loc - 1 + + // Data + val localX = tile shr 6 and 0x3f + val localY = tile and 0x3f + var level = tile shr 12 + val data = reader.readUnsignedByte() + + // Decrease bridges + if (isBridge(tiles, localX, localY)) { + level-- + } + + // Validate level + if (level !in 0 until 4) { + continue + } + + val shape = data shr 2 + val rotation = data and 0x3 + + // Valid object + add(objectId, localX, localY, level, shape, rotation, regionTileX, regionTileY) + } + } + } + + abstract fun add(objectId: Int, localX: Int, localY: Int, level: Int, shape: Int, rotation: Int, regionTileX: Int, regionTileY: Int) + + companion object { + private const val BRIDGE_TILE = 0x2 + + private fun isBridge(tiles: LongArray, localX: Int, localY: Int): Boolean { + return MapTile.settings(tiles[MapDefinition.index(localX, localY, 1)]) and BRIDGE_TILE == BRIDGE_TILE + } + } +} diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/MapObjectDefinitionDecoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/MapObjectDefinitionDecoder.kt new file mode 100644 index 0000000000..3e640abfd9 --- /dev/null +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/MapObjectDefinitionDecoder.kt @@ -0,0 +1,30 @@ +package world.gregs.voidps.cache.definition.decoder + +import world.gregs.voidps.buffer.read.BufferReader +import world.gregs.voidps.cache.Cache +import world.gregs.voidps.cache.Index +import world.gregs.voidps.cache.definition.data.MapDefinition +import world.gregs.voidps.cache.definition.data.MapObject + +/** + * Adds all objects except bridges to a [MapDefinition] + */ +class MapObjectDefinitionDecoder( + val xteas: Map? = null +) : MapObjectDecoder() { + + lateinit var definition: MapDefinition + + fun decode(cache: Cache, definition: MapDefinition) { + this.definition = definition + val regionX = definition.id shr 8 + val regionY = definition.id and 0xff + val objectData = cache.data(Index.MAPS, "l${regionX}_$regionY", xteas?.get(definition.id)) ?: return + val reader = BufferReader(objectData) + super.decode(reader, definition.tiles, -1, -1) + } + + override fun add(objectId: Int, localX: Int, localY: Int, level: Int, shape: Int, rotation: Int, regionTileX: Int, regionTileY: Int) { + definition.objects.add(MapObject(objectId, localX, localY, level, shape, rotation)) + } +} diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/MapTileDecoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/MapTileDecoder.kt new file mode 100644 index 0000000000..9dcbe7ccd0 --- /dev/null +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/MapTileDecoder.kt @@ -0,0 +1,55 @@ +package world.gregs.voidps.cache.definition.decoder + +import world.gregs.voidps.buffer.read.Reader +import world.gregs.voidps.cache.definition.data.MapDefinition +import world.gregs.voidps.cache.definition.data.MapTile + +/** + * Loads all + */ +object MapTileDecoder { + fun loadTiles(reader: Reader, tiles: LongArray) { + for (level in 0 until 4) { + for (localX in 0 until 64) { + for (localY in 0 until 64) { + var height = 0 + var attrOpcode = 0 + var overlayPath = 0 + var overlayRotation = 0 + var overlayId = 0 + var settings = 0 + var underlayId = 0 + loop@ while (true) { + val config = reader.readUnsignedByte() + if (config == 0) { + break@loop + } else if (config == 1) { + height = reader.readUnsignedByte() + break@loop + } else if (config <= 49) { + attrOpcode = config + overlayId = reader.readUnsignedByte() + overlayPath = (config - 2) / 4 + overlayRotation = 3 and (config - 2) + } else if (config <= 81) { + settings = config - 49 + } else { + underlayId = (config - 81) and 0xff + } + } + if (height != 0 || attrOpcode != 0 || overlayPath != 0 || overlayRotation != 0 || overlayId != 0 || settings != 0 || underlayId != 0) { + tiles[MapDefinition.index(localX, localY, level)] = MapTile.pack( + height, + attrOpcode, + overlayId, + overlayPath, + overlayRotation, + settings, + underlayId + ) + } + } + } + } + } +} \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/QuickChatOptionDecoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/QuickChatOptionDecoder.kt index 2f6676e4b2..467f2e0086 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/QuickChatOptionDecoder.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/QuickChatOptionDecoder.kt @@ -24,9 +24,9 @@ class QuickChatOptionDecoder : DefinitionDecoder(QUIC val archive = getArchive(id) val file = getFile(id) val data = (if (file <= 0x7fff) { - cache.getFile(index, archive, file) + cache.data(index, archive, file) } else { - cache.getFile(QUICK_CHAT_MENUS, archive, file and 0x7fff) + cache.data(QUICK_CHAT_MENUS, archive, file and 0x7fff) }) ?: return read(definitions, id, BufferReader(data)) } diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/QuickChatPhraseDecoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/QuickChatPhraseDecoder.kt index 955b1c2b56..8bbca4ec17 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/QuickChatPhraseDecoder.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/QuickChatPhraseDecoder.kt @@ -29,9 +29,9 @@ class QuickChatPhraseDecoder : DefinitionDecoder(QUIC val archive = getArchive(id) val file = getFile(id) val data = (if (file <= 0x7fff) { - cache.getFile(index, archive, file) + cache.data(index, archive, file) } else { - cache.getFile(QUICK_CHAT_MENUS, archive, file and 0x7fff) + cache.data(QUICK_CHAT_MENUS, archive, file and 0x7fff) }) ?: return read(definitions, id, BufferReader(data)) } diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/TextureDecoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/TextureDecoder.kt index f2147648b0..f1fb33e4e3 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/TextureDecoder.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/TextureDecoder.kt @@ -15,9 +15,9 @@ class TextureDecoder : DefinitionDecoder(TEXTURE_DEFINITIONS) override fun getFile(id: Int) = 0 - override fun loadCache(cache: Cache): Array { + override fun load(cache: Cache): Array { val start = System.currentTimeMillis() - val data = cache.getFile(index, 0, 0)!! + val data = cache.data(index, 0, 0)!! val reader = BufferReader(data) val size = reader.readShort() val definitions = create(size) diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/VarBitDecoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/VarBitDecoder.kt index 15635a4c35..5ebfb02b39 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/VarBitDecoder.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/VarBitDecoder.kt @@ -9,7 +9,7 @@ import world.gregs.voidps.cache.definition.data.VarBitDefinition class VarBitDecoder : DefinitionDecoder(VAR_BIT) { override fun size(cache: Cache): Int { - return cache.lastArchiveId(index) * 0x400 + cache.archiveCount(index, cache.lastArchiveId(index)) + return cache.lastArchiveId(index) * 0x400 + cache.fileCount(index, cache.lastArchiveId(index)) } override fun create(size: Int) = Array(size) { VarBitDefinition(it) } diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/WorldMapDetailsDecoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/WorldMapDetailsDecoder.kt index b58126496d..a0a1ab3509 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/WorldMapDetailsDecoder.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/WorldMapDetailsDecoder.kt @@ -12,13 +12,13 @@ import java.util.* class WorldMapDetailsDecoder : DefinitionDecoder(WORLD_MAP) { override fun size(cache: Cache): Int { - return cache.lastFileId(index, cache.getArchiveId(index, "details")) + return cache.lastFileId(index, cache.archiveId(index, "details")) } override fun load(definitions: Array, cache: Cache, id: Int) { - val archive = cache.getArchiveId(index, "details") + val archive = cache.archiveId(index, "details") val file = getFile(id) - val data = cache.getFile(index, archive, file) ?: return + val data = cache.data(index, archive, file) ?: return read(definitions, id, BufferReader(data)) } diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/WorldMapIconDecoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/WorldMapIconDecoder.kt index 70267103e2..f9a15b7a58 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/WorldMapIconDecoder.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/WorldMapIconDecoder.kt @@ -23,14 +23,14 @@ class WorldMapIconDecoder : DefinitionDecoder(WORLD_MAP) override fun load(definitions: Array, cache: Cache, id: Int) { val archive = getArchive(id) - var length = cache.archiveCount(index, archive) + var length = cache.fileCount(index, archive) var counter = 0 var index = 0 if (length > 0) { val definition = definitions[id] val icons = mutableListOf() while (length > counter) { - val data = cache.getFile(this.index, archive, index++) ?: continue + val data = cache.data(this.index, archive, index++) ?: continue val buffer = BufferReader(data) val position = buffer.readInt() val iconId = buffer.readShort() diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/encoder/ClientScriptEncoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/encoder/ClientScriptEncoder.kt index 51a1028175..359923c34c 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/encoder/ClientScriptEncoder.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/encoder/ClientScriptEncoder.kt @@ -4,7 +4,7 @@ import world.gregs.voidps.buffer.write.Writer import world.gregs.voidps.cache.DefinitionEncoder import world.gregs.voidps.cache.definition.data.ClientScriptDefinition -class ClientScriptEncoder(private val revision634: Boolean = false) : DefinitionEncoder { +class ClientScriptEncoder(private val revision667: Boolean = false) : DefinitionEncoder { override fun Writer.encode(definition: ClientScriptDefinition) { if (definition.id == -1) { @@ -15,9 +15,9 @@ class ClientScriptEncoder(private val revision634: Boolean = false) : Definition writeShort(instruction) when (instruction) { 3 -> writeString(definition.stringOperands!![index]) - 54 -> if (!revision634) writeLong(definition.longOperands!![index]) + 54 -> if (revision667) writeLong(definition.longOperands!![index]) else -> { - if (instruction >= (if (revision634) 100 else 150) || instruction == 21 || instruction == 38 || instruction == 39) { + if (instruction >= (if (revision667) 150 else 100) || instruction == 21 || instruction == 38 || instruction == 39) { writeByte(definition.intOperands!![index]) } else { writeInt(definition.intOperands!![index]) @@ -28,12 +28,12 @@ class ClientScriptEncoder(private val revision634: Boolean = false) : Definition writeInt(definition.instructions.size) writeShort(definition.intVariableCount) writeShort(definition.stringVariableCount) - if (!revision634) { + if (revision667) { writeShort(definition.longVariableCount) } writeShort(definition.intArgumentCount) writeShort(definition.stringArgumentCount) - if (!revision634) { + if (revision667) { writeShort(definition.longArgumentCount) } val position = position() diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/encoder/InterfaceEncoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/encoder/InterfaceEncoder.kt index 6ea1e5cb7d..9e89ee8a32 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/encoder/InterfaceEncoder.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/encoder/InterfaceEncoder.kt @@ -4,14 +4,14 @@ import world.gregs.voidps.buffer.write.Writer import world.gregs.voidps.cache.DefinitionEncoder import world.gregs.voidps.cache.definition.data.InterfaceComponentDefinitionFull -internal class InterfaceEncoder : DefinitionEncoder { +class InterfaceEncoder : DefinitionEncoder { override fun Writer.encode(definition: InterfaceComponentDefinitionFull) { if (definition.id == -1) { return } writeByte(-1) - if(!definition.unknown.isNullOrEmpty()) { + if (!definition.unknown.isNullOrEmpty()) { writeByte(definition.type or 0x80) writeString(definition.unknown) } else { @@ -85,10 +85,10 @@ internal class InterfaceEncoder : DefinitionEncoder 1) { - //TODO + if (icons != null && icons.isNotEmpty()) { + writeByte(icons.lastIndex) + for (index in icons.indices) { + writeShort(icons[index]) } } diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/secure/Xtea.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/secure/Xtea.kt index 9bdfa050bd..e5a967a1a9 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/secure/Xtea.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/secure/Xtea.kt @@ -23,12 +23,12 @@ object Xtea { * @throws IllegalArgumentException if the key is not exactly 4 elements * long. */ - fun decipher(buffer: ByteArray, key: IntArray, start: Int = 0) { + fun decipher(buffer: ByteArray, key: IntArray, start: Int = 0, end: Int = buffer.size) { if (key.size != 4) { throw IllegalArgumentException() } - val numQuads = buffer.size / 8 + val numQuads = (end - start) / 8 for (i in 0 until numQuads) { var sum = GOLDEN_RATIO * ROUNDS var v0 = getInt(buffer, start + i * 8) diff --git a/cache/src/test/kotlin/world/gregs/voidps/cache/active/encode/ZoneObjectTest.kt b/cache/src/test/kotlin/world/gregs/voidps/cache/active/encode/ZoneObjectTest.kt deleted file mode 100644 index 046133119b..0000000000 --- a/cache/src/test/kotlin/world/gregs/voidps/cache/active/encode/ZoneObjectTest.kt +++ /dev/null @@ -1,36 +0,0 @@ -package world.gregs.voidps.cache.active.encode - -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import world.gregs.voidps.type.Tile - -class ZoneObjectTest { - - @Test - fun `Get values from hash`() { - val value = ZoneObject.pack(43200, 7, 6, 3, 22, 3) - assertEquals(43200, ZoneObject.id(value)) - assertEquals(7, ZoneObject.x(value)) - assertEquals(6, ZoneObject.y(value)) - assertEquals(3, ZoneObject.level(value)) - assertEquals(22, ZoneObject.shape(value)) - assertEquals(3, ZoneObject.rotation(value)) - } - - @Test - fun `Get tile from hash`() { - val value = ZoneObject.pack(43200, 7, 6, 3, 22, 3) - val tile = ZoneObject.tile(value) - assertEquals(7, Tile.indexX(tile)) - assertEquals(6, Tile.indexY(tile)) - } - - @Test - fun `Get info from hash`() { - val value = ZoneObject.pack(51213, 0, 0, 0, 1, 1) - val info = ZoneObject.info(value) - assertEquals(51213, ZoneObject.infoId(info)) - assertEquals(1, ZoneObject.infoShape(info)) - assertEquals(1, ZoneObject.infoRotation(info)) - } -} \ No newline at end of file diff --git a/cache/src/test/kotlin/world/gregs/voidps/cache/definition/decoder/MapDecoderTest.kt b/cache/src/test/kotlin/world/gregs/voidps/cache/definition/decoder/MapDecoderTest.kt index c87cce7b8d..fd8225645b 100644 --- a/cache/src/test/kotlin/world/gregs/voidps/cache/definition/decoder/MapDecoderTest.kt +++ b/cache/src/test/kotlin/world/gregs/voidps/cache/definition/decoder/MapDecoderTest.kt @@ -32,8 +32,8 @@ internal class MapDecoderTest { data[66] = 51 data[2] = 52 data[4099] = 53 - every { cache.getFile(5, 123, 0, null) } returns data - every { cache.getFile(5, "l48_57", any()) } returns null + every { cache.data(5, 123, 0, null) } returns data + every { cache.data(5, "l48_57", any()) } returns null decoder.load(definitions, cache, 123) val def = definitions[123] @@ -48,8 +48,8 @@ internal class MapDecoderTest { fun `Read object ignores invalid levels`() { val tileData = ByteArray((4 * 64 * 64) + 4) val objectData = byteArrayOf(-80, 58, -64, 66, 17, 0, 0) - every { cache.getFile(5, 123, 0, null) } returns tileData - every { cache.getFile(5, "l48_57", any()) } returns objectData + every { cache.data(5, 123, 0, null) } returns tileData + every { cache.data(5, "l48_57", any()) } returns objectData decoder.load(definitions, cache, 123) val def = definitions[123] @@ -61,8 +61,8 @@ internal class MapDecoderTest { fun `Read object ignores locations out of region`() { val tileData = ByteArray((4 * 64 * 64) + 4) val objectData = byteArrayOf(-80, 58, -112, 65, 0, 0, 0) - every { cache.getFile(5, 123, 0, null) } returns tileData - every { cache.getFile(5, "l48_57", any()) } returns objectData + every { cache.data(5, 123, 0, null) } returns tileData + every { cache.data(5, "l48_57", any()) } returns objectData decoder.load(definitions, cache, 123) val def = definitions[123] @@ -74,8 +74,8 @@ internal class MapDecoderTest { fun `Read two objects with the same tile`() { val tileData = ByteArray((4 * 64 * 64) + 4) val objectData = byteArrayOf(-80, 58, -115, -82, 50, 0, -13, -41, -115, -82, 0, 0, 0) - every { cache.getFile(5, 123, 0, null) } returns tileData - every { cache.getFile(5, "l48_57", any()) } returns objectData + every { cache.data(5, 123, 0, null) } returns tileData + every { cache.data(5, "l48_57", any()) } returns objectData decoder.load(definitions, cache, 123) val def = definitions[123] @@ -88,8 +88,8 @@ internal class MapDecoderTest { fun `Read two objects with the same id`() { val tileData = ByteArray((4 * 64 * 64) + 4) val objectData = byteArrayOf(-80, 58, 1, 50, -64, 0, 17, 0, 0) - every { cache.getFile(5, 123, 0, null) } returns tileData - every { cache.getFile(5, "l48_57", any()) } returns objectData + every { cache.data(5, 123, 0, null) } returns tileData + every { cache.data(5, "l48_57", any()) } returns objectData decoder.load(definitions, cache, 123) val def = definitions[123] diff --git a/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/ClientScriptEncoderTest.kt b/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/ClientScriptEncoderTest.kt index 5e4dec3fc3..abdffd0e9d 100644 --- a/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/ClientScriptEncoderTest.kt +++ b/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/ClientScriptEncoderTest.kt @@ -29,8 +29,8 @@ internal class ClientScriptEncoderTest { longOperands = longArrayOf(0, 1, 2, 3, 0, 0), intOperands = intArrayOf(0, 0, 0, 0, 300, 2) ) - val revision634 = false - val encoder = ClientScriptEncoder(revision634) + val revision667 = true + val encoder = ClientScriptEncoder(revision667) val writer = BufferWriter(capacity = 256) with(encoder) { @@ -40,9 +40,9 @@ internal class ClientScriptEncoderTest { val data = writer.array().copyOf(writer.position()) val cache: Cache = mockk(relaxed = true) - every { cache.getFile(CLIENT_SCRIPTS, any(), any(), any()) } returns data + every { cache.data(CLIENT_SCRIPTS, any(), any(), any()) } returns data every { cache.lastArchiveId(any()) } returns 1 - val decoder = ClientScriptDecoder(revision634).loadCache(cache) + val decoder = ClientScriptDecoder(revision667).load(cache) val decoded = decoder[0] assertEquals(definition, decoded) } @@ -61,8 +61,8 @@ internal class ClientScriptEncoderTest { stringOperands = arrayOf("one", null, null), intOperands = intArrayOf(0, 300, 2) ) - val revision634 = true - val encoder = ClientScriptEncoder(revision634) + val revision667 = false + val encoder = ClientScriptEncoder(revision667) val writer = BufferWriter(capacity = 256) with(encoder) { @@ -72,9 +72,9 @@ internal class ClientScriptEncoderTest { val data = writer.array().copyOf(writer.position()) val cache: Cache = mockk(relaxed = true) - every { cache.getFile(CLIENT_SCRIPTS, any(), any()) } returns data + every { cache.data(CLIENT_SCRIPTS, any(), any()) } returns data every { cache.lastArchiveId(any()) } returns 1 - val decoder = ClientScriptDecoder(revision634).loadCache(cache) + val decoder = ClientScriptDecoder(revision667).load(cache) val decoded = decoder[0] assertEquals(definition, decoded) } diff --git a/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/EncoderComparator.kt b/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/EncoderComparator.kt index 07af497790..d7d57e271f 100644 --- a/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/EncoderComparator.kt +++ b/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/EncoderComparator.kt @@ -32,14 +32,14 @@ class EncoderComparator { }) } val decoder = ItemDecoder() - every { cache.getFile(ITEMS, archive = any(), file = any()) } answers { + every { cache.data(ITEMS, archive = any(), file = any()) } answers { if (arg(1) == decoder.getArchive(0) && arg(2) == decoder.getFile(0)) { data } else { null } } - val defs = decoder.loadCache(cache).getOrNull(0) + val defs = decoder.load(cache).getOrNull(0) println("Expected $definition") println("Actual $defs") } diff --git a/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/InterfaceEncoderTest.kt b/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/InterfaceEncoderTest.kt index cc2108f121..d283bfc8ac 100644 --- a/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/InterfaceEncoderTest.kt +++ b/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/InterfaceEncoderTest.kt @@ -30,7 +30,7 @@ internal class InterfaceEncoderTest { verticalPositionMode = 1, parent = 1, hidden = true, - keyRepeat = null, + keyRepeats = null, keyCodes = null, keyModifiers = null, name = "Applied", @@ -58,9 +58,9 @@ internal class InterfaceEncoderTest { val data = writer.toArray() val cache: Cache = mockk(relaxed = true) - every { cache.getFile(INTERFACES, any(), any()) } returns data + every { cache.data(INTERFACES, any(), any()) } returns data every { cache.lastArchiveId(any()) } returns 1 - val decoder = InterfaceDecoderFull().loadCache(cache) + val decoder = InterfaceDecoderFull().load(cache) val inter = decoder[0] val decoded = inter.components?.get(0) assertEquals(definition, decoded) diff --git a/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/ItemEncoderTest.kt b/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/ItemEncoderTest.kt index 0cc0ad6ffd..e83a0ab3a7 100644 --- a/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/ItemEncoderTest.kt +++ b/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/ItemEncoderTest.kt @@ -102,7 +102,7 @@ internal class ItemEncoderTest { fun `Encode everything`() { val cache: Cache = CacheDelegate("../data/cache/") val decoder = ItemDecoderFull() - val full = decoder.loadCache(cache) + val full = decoder.load(cache) val encoder = ItemEncoder() val writer = BufferWriter(1024) diff --git a/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/NPCEncoderTest.kt b/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/NPCEncoderTest.kt index c84dbf9570..70e8bc335c 100644 --- a/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/NPCEncoderTest.kt +++ b/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/NPCEncoderTest.kt @@ -109,7 +109,7 @@ internal class NPCEncoderTest { fun `Encode everything`() { val cache: Cache = CacheDelegate("../data/cache/") val decoder = NPCDecoderFull() - val full = decoder.loadCache(cache) + val full = decoder.load(cache) val encoder = NPCEncoder() val writer = BufferWriter(1024) for (definition in full) { diff --git a/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/ObjectEncoderTest.kt b/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/ObjectEncoderTest.kt index 201510f627..38de0a9e22 100644 --- a/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/ObjectEncoderTest.kt +++ b/cache/src/test/kotlin/world/gregs/voidps/cache/definition/encoder/ObjectEncoderTest.kt @@ -123,7 +123,7 @@ internal class ObjectEncoderTest { fun `Encode everything`() { val cache: Cache = CacheDelegate("../data/cache/") val decoder = ObjectDecoderFull() - val full = decoder.loadCache(cache) + val full = decoder.load(cache) val encoder = ObjectEncoder() val writer = BufferWriter(1024) for (definition in full) { diff --git a/data/definitions/interfaces.yml b/data/definitions/interfaces.yml index d2cb078060..f5bb37b513 100644 --- a/data/definitions/interfaces.yml +++ b/data/definitions/interfaces.yml @@ -1861,6 +1861,7 @@ equipment_bonuses: magic_damage: 47 bank: 49 stats: 52 + stats_done: 64 close: 75 dialogue_pick_a_puppy: id: 668 diff --git a/data/xteas.dat b/data/xteas.dat deleted file mode 100644 index ed9443ca4a..0000000000 Binary files a/data/xteas.dat and /dev/null differ diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/EngineModules.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/EngineModules.kt index 6c85280f30..8ff71513e3 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/EngineModules.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/EngineModules.kt @@ -21,7 +21,6 @@ import world.gregs.voidps.engine.event.EventHandlerStore import world.gregs.voidps.engine.map.collision.CollisionStrategyProvider import world.gregs.voidps.engine.map.collision.Collisions import world.gregs.voidps.engine.map.collision.GameObjectCollision -import world.gregs.voidps.engine.map.region.Xteas import world.gregs.voidps.engine.map.zone.DynamicZones import world.gregs.voidps.type.Tile import world.gregs.yaml.Yaml @@ -47,7 +46,6 @@ val engineModule = module { single { DynamicZones(get(), get(), get()) } single { EventHandlerStore() } single(createdAtStart = true) { AreaDefinitions().load() } - single(createdAtStart = true) { Xteas().load() } // Network single { ConnectionQueue(getIntProperty("connectionPerTickCap", 1)) diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/MapDefinitions.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/MapDefinitions.kt index bd2e0fe79c..11c87d5cf3 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/MapDefinitions.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/MapDefinitions.kt @@ -1,272 +1,70 @@ package world.gregs.voidps.engine.data.definition import com.github.michaelbull.logging.InlineLogger -import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap -import org.rsmod.game.pathfinder.flag.CollisionFlag import world.gregs.voidps.buffer.read.BufferReader -import world.gregs.voidps.buffer.read.Reader -import world.gregs.voidps.cache.Cache -import world.gregs.voidps.cache.CacheDelegate -import world.gregs.voidps.cache.Index -import world.gregs.voidps.cache.active.ActiveCache -import world.gregs.voidps.cache.active.encode.ZoneObject -import world.gregs.voidps.cache.definition.decoder.MapDecoder -import world.gregs.voidps.cache.definition.decoder.ObjectDecoder +import world.gregs.voidps.cache.* +import world.gregs.voidps.cache.definition.decoder.MapTileDecoder import world.gregs.voidps.engine.client.ui.chat.plural -import world.gregs.voidps.engine.client.update.batch.ZoneBatchUpdates import world.gregs.voidps.engine.entity.obj.GameObjects -import world.gregs.voidps.engine.map.collision.CollisionReader -import world.gregs.voidps.engine.map.collision.Collisions -import world.gregs.voidps.engine.map.collision.GameObjectCollision -import world.gregs.voidps.engine.map.region.Xteas +import world.gregs.voidps.engine.map.collision.CollisionDecoder +import world.gregs.voidps.engine.map.obj.MapObjectsDecoder +import world.gregs.voidps.engine.map.obj.MapObjectsRotatedDecoder import world.gregs.voidps.type.Region -import world.gregs.voidps.type.RegionLevel -import world.gregs.voidps.type.Tile import world.gregs.voidps.type.Zone -import world.gregs.yaml.Yaml -import java.io.File -import java.io.RandomAccessFile -import kotlin.collections.set /** - * Loads map collision and objects from [ActiveCache] index created by [MapEncoder] + * Loads map collision and objects directly, quicker than [MapDecoder] + * + * Note: this is the only place we store the cache; for dynamic zone loading. */ class MapDefinitions( - private val collisions: Collisions, - private val definitions: ObjectDefinitions, - private val objects: GameObjects + private val collisions: CollisionDecoder, + definitions: ObjectDefinitions, + private val objects: GameObjects, + private val cache: Cache ) { private val logger = InlineLogger() - private val objectIndices: MutableMap = Int2IntOpenHashMap(70_000) - private val tileIndices: MutableMap = Int2IntOpenHashMap(90_000) - private lateinit var raf: RandomAccessFile - private val objectArray = ByteArray(2048) - private val tileArray = ByteArray(12) - private var position = 0 - fun loadCache(cache: Cache, xteas: Xteas): MapDefinitions { - val start = System.currentTimeMillis() - val maps = MapDecoder(xteas).loadCache(cache) - val reader = CollisionReader(collisions) - for (map in maps) { - val region = Region(map.id) - reader.read(region, map) - val regionTileX = region.tile.x - val regionTileY = region.tile.y - for (obj in map.objects) { - val def = definitions.get(obj.id) - objects.set(obj.id, regionTileX + obj.x, regionTileY + obj.y, obj.level, obj.shape, obj.rotation, def) - } - } - logger.info { "Loaded ${maps.size} maps ${objects.size} ${"object".plural(objects.size)} from cache in ${System.currentTimeMillis() - start}ms" } - return this - } + private val decoder = MapObjectsDecoder(objects, definitions) + private val rotationDecoder = MapObjectsRotatedDecoder(objects, definitions) - fun load(directory: File): MapDefinitions { + fun loadCache(xteas: Map? = null): MapDefinitions { val start = System.currentTimeMillis() - val file = directory.resolve(ActiveCache.indexFile(Index.MAPS)) - val reader = BufferReader(file.readBytes()) - val regions = reader.readInt() - readEmptyTiles(reader) - readTiles(reader) - position = reader.position() - readFullTiles(reader) - readObjects(reader) - logger.info { "Loaded $regions maps ${objects.size} ${"object".plural(objects.size)} from file in ${System.currentTimeMillis() - start}ms" } - raf = RandomAccessFile(file, "r") - return this - } - - private fun readEmptyTiles(reader: BufferReader) { - for (i in 0 until reader.readInt()) { - val regionLevel = RegionLevel(reader.readInt()) - val regionTileX = regionLevel.tile.x - val regionTileY = regionLevel.tile.y - val level = regionLevel.level - for (zoneTileX in 0 until 64 step 8) { - for (zoneTileY in 0 until 64 step 8) { - val x = regionTileX + zoneTileX - val y = regionTileY + zoneTileY - collisions.allocateIfAbsent(x, y, level) - } - } - } - for (i in 0 until reader.readInt()) { - val zone = Zone(reader.readInt()).tile - collisions.allocateIfAbsent(zone.x, zone.y, zone.level) - } - } - - private fun readTiles(reader: BufferReader) { - for (i in 0 until reader.readInt()) { - val zoneIndex = reader.readInt() - tileIndices[zoneIndex] = reader.position() - val value = reader.readLong() - collisions.flags[zoneIndex] = IntArray(ZONE_SIZE) { - if (value ushr it and 0x1 == 1L) CollisionFlag.FLOOR else 0 - } - } - } - - private fun readObjects(reader: BufferReader) { - for (i in 0 until reader.readInt()) { - val zoneIndex = reader.readInt() - objectIndices[zoneIndex] = reader.position() - for (j in 0 until reader.readShort()) { - val obj = ZoneObject(reader.readInt()) - val def = definitions.getValue(obj.id) - objects.set(obj, zoneIndex, def) + var regions = 0 + for (regionX in 0 until 256) { + for (regionY in 0 until 256) { + val tiles = loadTiles(cache, regionX, regionY) ?: continue + collisions.decode(tiles, regionX shl 6, regionY shl 6) + val keys = if (xteas != null) xteas[Region.id(regionX, regionY)] else null + decoder.decode(cache, tiles, regionX, regionY, keys) + regions++ } } + logger.info { "Loaded $regions maps ${objects.size} ${"object".plural(objects.size)} in ${System.currentTimeMillis() - start}ms" } + return this } - private fun readFullTiles(reader: BufferReader) { - for (i in 0 until reader.readInt()) { - val zoneIndex = reader.readInt() - tileIndices[zoneIndex] = reader.position() - fillTiles(zoneIndex) - } - } - - fun loadZone(from: Zone, to: Zone, rotation: Int) { + fun loadZone(from: Zone, to: Zone, rotation: Int, xteas: Map? = null) { val start = System.currentTimeMillis() - val tilePosition = tileIndices[from.id] - if (tilePosition == null) { - collisions.allocateIfAbsent( - absoluteX = to.tile.x, - absoluteZ = to.tile.y, - level = to.level - ) - } else if (tilePosition > position) { - fillTiles(to.id) - } else { - raf.seek(tilePosition.toLong()) - raf.read(tileArray) - val reader = BufferReader(tileArray) - readTiles(to, reader, rotation) - } - val objectPosition = objectIndices[from.id] - if (objectPosition != null) { - raf.seek(objectPosition.toLong()) - raf.read(objectArray) - val reader = BufferReader(objectArray) - readObjects(to, reader, rotation) - } + val tiles = loadTiles(cache, from.region.x, from.region.y) ?: return + collisions.decode(tiles, from, to, rotation) + val keys = if (xteas != null) xteas[from.region.id] else null + rotationDecoder.decode(cache, tiles, from, to, rotation, keys) val took = System.currentTimeMillis() - start if (took > 5) { logger.info { "Loaded zone $from -> $to $rotation in ${took}ms" } } } - private fun readObjects(zone: Zone, reader: Reader, zoneRotation: Int) { - val zoneTileX = zone.tile.x - val zoneTileY = zone.tile.y - for (i in 0 until reader.readShort()) { - val obj = ZoneObject(reader.readInt()) - val def = definitions.get(obj.id) - val rotation = (obj.rotation + zoneRotation) and 0x3 - val rotX = zoneTileX + rotateX(obj.x, obj.y, def.sizeX, def.sizeY, rotation, zoneRotation) - val rotY = zoneTileY + rotateY(obj.x, obj.y, def.sizeX, def.sizeY, rotation, zoneRotation) - objects.set(obj.id, rotX, rotY, obj.level, obj.shape, rotation, def) - } - } - - private fun readTiles(zone: Zone, reader: Reader, zoneRotation: Int) { - val intArray = collisions.allocateIfAbsent( - absoluteX = zone.tile.x, - absoluteZ = zone.tile.y, - level = zone.level - ) - val value = reader.readLong() - for (i in 0 until ZONE_SIZE) { - if (value ushr i and 0x1 == 1L) { - val x = Tile.indexX(i) - val y = Tile.indexY(i) - val index = Tile.index(rotateX(x, y, zoneRotation), rotateY(x, y, zoneRotation)) - intArray[index] = intArray[i] or CollisionFlag.FLOOR - } - } - } - - private fun fillTiles(zoneIndex: Int) { - val array = collisions.flags[zoneIndex] - if (array == null) { - collisions.flags[zoneIndex] = IntArray(ZONE_SIZE) { CollisionFlag.FLOOR } - return - } - array.fill(CollisionFlag.FLOOR) - } - - companion object { - private const val ZONE_SIZE = 64 - - private fun rotateX(x: Int, y: Int, rotation: Int): Int { - return (if (rotation == 1) y else if (rotation == 2) 7 - x else if (rotation == 3) 7 - y else x) and 0x7 - } - - private fun rotateY(x: Int, y: Int, rotation: Int): Int { - return (if (rotation == 1) 7 - x else if (rotation == 2) 7 - y else if (rotation == 3) x else y) and 0x7 - } - - private fun rotateX( - objX: Int, - objY: Int, - sizeX: Int, - sizeY: Int, - objRotation: Int, - zoneRotation: Int - ): Int { - var x = sizeX - var y = sizeY - val rotation = zoneRotation and 0x3 - if (objRotation and 0x1 == 1) { - val temp = x - x = y - y = temp - } - if (rotation == 0) { - return objX - } - if (rotation == 1) { - return objY - } - return if (rotation == 2) 7 - objX - x + 1 else 7 - objY - y + 1 - } - - private fun rotateY( - objX: Int, - objY: Int, - sizeX: Int, - sizeY: Int, - objRotation: Int, - zoneRotation: Int - ): Int { - val rotation = zoneRotation and 0x3 - var x = sizeY - var y = sizeX - if (objRotation and 0x1 == 1) { - val temp = y - y = x - x = temp - } - if (rotation == 0) { - return objY - } - if (rotation == 1) { - return 7 - objX - y + 1 - } - return if (rotation == 2) 7 - objY - x + 1 else objX - } - - @JvmStatic - fun main(args: Array) { - val cache = CacheDelegate("./data/cache") - val objectDefinitions = ObjectDefinitions(ObjectDecoder(member = true, lowDetail = false).loadCache(cache)) - .load(Yaml(), "./data/definitions/objects.yml") - val collisions = Collisions() - val objects = GameObjects(GameObjectCollision(collisions), ZoneBatchUpdates(), objectDefinitions, storeUnused = true) - val mapDefinitions = MapDefinitions(collisions, objectDefinitions, objects) - mapDefinitions.load(File("./data/cache/active/")) + private fun loadTiles(cache: Cache, regionX: Int, regionY: Int): LongArray? { + val archive = cache.archiveId(Index.MAPS, "m${regionX}_$regionY") + if (archive == -1) { + return null } + val data = cache.data(Index.MAPS, archive) ?: return null + val buffer = BufferReader(data) + val tiles = LongArray(16384) + MapTileDecoder.loadTiles(buffer, tiles) + return tiles } } \ No newline at end of file diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/obj/GameObjectArrayMap.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/obj/GameObjectArrayMap.kt index d96eeb38a5..0c8ab9b5a2 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/obj/GameObjectArrayMap.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/obj/GameObjectArrayMap.kt @@ -19,11 +19,6 @@ class GameObjectArrayMap : GameObjectMap { return data[zoneIndex]?.get(tileIndex) ?: -1 } - override operator fun set(zone: Int, tile: Int, mask: Int) { - val tiles = data[zone] ?: allocateIfAbsent(zone) - tiles[tile] = mask - } - override operator fun set(x: Int, y: Int, level: Int, layer: Int, mask: Int) { val tiles = data[Zone.tileIndex(x, y, level)] ?: allocateIfAbsent(x, y, level) tiles[Tile.index(x, y, layer)] = mask diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/obj/GameObjectHashMap.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/obj/GameObjectHashMap.kt index 89fc9040e5..c4d4e53441 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/obj/GameObjectHashMap.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/obj/GameObjectHashMap.kt @@ -19,10 +19,6 @@ class GameObjectHashMap : GameObjectMap { return data.getOrDefault(index(x, y, level, layer), -1) } - override fun set(zone: Int, tile: Int, mask: Int) { - data[index(zone, tile)] = mask - } - override operator fun set(x: Int, y: Int, level: Int, layer: Int, mask: Int) { data[index(x, y, level, layer)] = mask } diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/obj/GameObjectMap.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/obj/GameObjectMap.kt index 58cce3addf..7dc0709d82 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/obj/GameObjectMap.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/obj/GameObjectMap.kt @@ -9,8 +9,6 @@ interface GameObjectMap { operator fun get(x: Int, y: Int, level: Int, layer: Int): Int - operator fun set(zone: Int, tile: Int, mask: Int) - operator fun set(x: Int, y: Int, level: Int, layer: Int, mask: Int) fun add(obj: GameObject, mask: Int) diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/obj/GameObjects.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/obj/GameObjects.kt index d3d8788a20..8d9dcf1e26 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/obj/GameObjects.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/obj/GameObjects.kt @@ -1,7 +1,6 @@ package world.gregs.voidps.engine.entity.obj import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap -import world.gregs.voidps.cache.active.encode.ZoneObject import world.gregs.voidps.cache.definition.data.ObjectDefinition import world.gregs.voidps.engine.client.ui.chat.toInt import world.gregs.voidps.engine.client.update.batch.ZoneBatchUpdates @@ -96,19 +95,6 @@ class GameObjects( } } - /** - * Sets the original placement of a game object (but faster) - */ - fun set(obj: ZoneObject, zoneIndex: Int, definition: ObjectDefinition) { - collisions.modify(obj, zoneIndex, definition) - if (interactive(definition)) { - val zone = zoneIndex or (obj.level shl 22) - val tile = ZoneObject.tile(obj.packed) or (ObjectLayer.layer(obj.shape) shl 6) - map[zone, tile] = ZoneObject.info(obj.packed) shl 1 - size++ - } - } - /** * Decide to store [GameObject]s which don't have options or configs * Skipping unused objects uses less ram but makes content creation harder. diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/map/collision/CollisionDecoder.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/map/collision/CollisionDecoder.kt new file mode 100644 index 0000000000..0dd7de27e1 --- /dev/null +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/map/collision/CollisionDecoder.kt @@ -0,0 +1,94 @@ +package world.gregs.voidps.engine.map.collision + +import org.rsmod.game.pathfinder.flag.CollisionFlag +import world.gregs.voidps.cache.definition.data.MapDefinition +import world.gregs.voidps.cache.definition.data.MapTile +import world.gregs.voidps.type.Region +import world.gregs.voidps.type.Zone + +/** + * Adds collision for all blocked tiles except bridges + */ +class CollisionDecoder(private val collisions: Collisions) { + + /** + * Decode into [MapDefinition.tiles] + */ + fun decode(region: Region, map: MapDefinition) { + val x = region.tile.x + val y = region.tile.y + decode(map.tiles, x, y) + } + + /** + * Decode [tiles] region [x] [y] into [Collisions] + */ + fun decode(tiles: LongArray, x: Int, y: Int) { + for (level in 0 until 4) { + for (localX in 0 until 64) { + for (localY in 0 until 64) { + if (localX.rem(8) == 0 && localY.rem(8) == 0) { + collisions.allocateIfAbsent(x + localX, y + localY, level) + } + if (!isTile(tiles, localX, localY, level, BLOCKED_TILE)) { + continue + } + val height = tileHeight(tiles, localX, localY, level) + if (height >= 0) { + collisions.add(x + localX, y + localY, height, CollisionFlag.FLOOR) + } + } + } + } + } + + /** + * Decode [from] Zone [tiles] into [Collisions] [to] with applied [zoneRotation] + */ + fun decode(tiles: LongArray, from: Zone, to: Zone, zoneRotation: Int) { + val x = from.tile.x.rem(64) + val y = from.tile.y.rem(64) + val targetX = to.tile.x + val targetY = to.tile.y + for (level in 0 until 4) { + collisions.allocateIfAbsent(targetX, targetY, level) + for (localX in x until x + 8) { + for (localY in y until y + 8) { + if (!isTile(tiles, localX, localY, level, BLOCKED_TILE)) { + continue + } + val height = tileHeight(tiles, localX, localY, level) + if (height >= 0) { + val rotX = rotateX(localX, localY, zoneRotation) + val rotY = rotateY(localX, localY, zoneRotation) + collisions.add(targetX + rotX, targetY + rotY, height, CollisionFlag.FLOOR) + } + } + } + } + } + + companion object { + internal const val BLOCKED_TILE = 0x1 + internal const val BRIDGE_TILE = 0x2 + + private fun tileHeight(tiles: LongArray, localX: Int, localY: Int, level: Int): Int { + if (isTile(tiles, localX, localY, 1, BRIDGE_TILE)) { + return level - 1 + } + return level + } + + private fun isTile(tiles: LongArray, localX: Int, localY: Int, level: Int, flag: Int): Boolean { + return MapTile.settings(tiles[MapDefinition.index(localX, localY, level)]) and flag == flag + } + + private fun rotateX(x: Int, y: Int, rotation: Int): Int { + return (if (rotation == 1) y else if (rotation == 2) 7 - x else if (rotation == 3) 7 - y else x) and 0x7 + } + + private fun rotateY(x: Int, y: Int, rotation: Int): Int { + return (if (rotation == 1) 7 - x else if (rotation == 2) 7 - y else if (rotation == 3) x else y) and 0x7 + } + } +} diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/map/collision/CollisionReader.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/map/collision/CollisionReader.kt deleted file mode 100644 index 6aac7514e8..0000000000 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/map/collision/CollisionReader.kt +++ /dev/null @@ -1,42 +0,0 @@ -package world.gregs.voidps.engine.map.collision - -import org.rsmod.game.pathfinder.flag.CollisionFlag -import world.gregs.voidps.cache.definition.data.MapDefinition -import world.gregs.voidps.type.Region - -/** - * Adds collision for all blocked tiles except bridges - */ -class CollisionReader(private val collisions: Collisions) { - - fun read(region: Region, map: MapDefinition) { - val x = region.tile.x - val y = region.tile.y - for (level in 0 until 4) { - for (localX in 0 until 64) { - for (localY in 0 until 64) { - if (localX.rem(8) == 0 && localY.rem(8) == 0) { - collisions.allocateIfAbsent(x + localX, y + localY, level) - } - val blocked = map.getTile(localX, localY, level).isTile(BLOCKED_TILE) - if (!blocked) { - continue - } - var height = level - val bridge = map.getTile(localX, localY, 1).isTile(BRIDGE_TILE) - if (bridge) { - height-- - } - if (height >= 0) { - collisions.add(x + localX, y + localY, height, CollisionFlag.FLOOR) - } - } - } - } - } - - companion object { - const val BLOCKED_TILE = 0x1 - const val BRIDGE_TILE = 0x2 - } -} diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/map/collision/GameObjectCollision.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/map/collision/GameObjectCollision.kt index 2617a9ba3b..bb48d36bd4 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/map/collision/GameObjectCollision.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/map/collision/GameObjectCollision.kt @@ -1,6 +1,5 @@ package world.gregs.voidps.engine.map.collision -import world.gregs.voidps.cache.active.encode.ZoneObject import world.gregs.voidps.cache.definition.data.ObjectDefinition import world.gregs.voidps.engine.entity.obj.GameObject import world.gregs.voidps.engine.entity.obj.ObjectShape @@ -28,23 +27,6 @@ class GameObjectCollision( } } - fun modify(obj: ZoneObject, zone: Int, def: ObjectDefinition) { - if (def.solid == 0) { - return - } - val x = obj.x + (Zone.x(zone) shl 3) - val y = obj.y + (Zone.y(zone) shl 3) - val level = obj.level - val rotation = obj.rotation - when (obj.shape) { - ObjectShape.WALL_STRAIGHT -> modifyWall(x, y, level, def.block, cardinal[(rotation + 3) and 0x3], true) - ObjectShape.WALL_DIAGONAL_CORNER, ObjectShape.WALL_SQUARE_CORNER -> modifyWall(x, y, level, def.block, ordinal[rotation], true) - ObjectShape.WALL_CORNER -> modifyWallCorner(x, y, level, def.block, ordinal[rotation], true) - in ObjectShape.WALL_DIAGONAL until ObjectShape.GROUND_DECOR -> modifyObject(def, x, y, level, rotation, def.block, true) - ObjectShape.GROUND_DECOR -> if (def.interactive == 1 && def.solid == 1) modifyCardinal(x, y, level, def.block, true) - } - } - private fun modifyWall(x: Int, y: Int, level: Int, block: Int, direction: Int, add: Boolean) { modifyTile(x, y, level, block, direction, add) modifyTile(x + deltaX[direction], y + deltaY[direction], level, block, inverse[direction], add) diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/map/obj/MapObjectsDecoder.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/map/obj/MapObjectsDecoder.kt new file mode 100644 index 0000000000..af848e55e9 --- /dev/null +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/map/obj/MapObjectsDecoder.kt @@ -0,0 +1,30 @@ +package world.gregs.voidps.engine.map.obj + +import world.gregs.voidps.buffer.read.BufferReader +import world.gregs.voidps.cache.Cache +import world.gregs.voidps.cache.Index +import world.gregs.voidps.cache.definition.decoder.MapObjectDecoder +import world.gregs.voidps.engine.data.definition.ObjectDefinitions +import world.gregs.voidps.engine.entity.obj.GameObjects + +/** + * Adds collision for all blocked tiles except bridges + */ +class MapObjectsDecoder( + private val objects: GameObjects, + private val definitions: ObjectDefinitions +) : MapObjectDecoder() { + + fun decode(cache: Cache, tiles: LongArray, regionX: Int, regionY: Int, keys: IntArray?) { + val objectData = cache.data(Index.MAPS, "l${regionX}_${regionY}", xtea = keys) ?: return + val reader = BufferReader(objectData) + super.decode(reader, tiles, regionX shl 6, regionY shl 6) + } + + override fun add(objectId: Int, localX: Int, localY: Int, level: Int, shape: Int, rotation: Int, regionTileX: Int, regionTileY: Int) { + if (objectId > definitions.definitions.size) { + return + } + objects.set(objectId, regionTileX + localX, regionTileY + localY, level, shape, rotation, definitions.getValue(objectId)) + } +} diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/map/obj/MapObjectsRotatedDecoder.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/map/obj/MapObjectsRotatedDecoder.kt new file mode 100644 index 0000000000..e1242e4498 --- /dev/null +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/map/obj/MapObjectsRotatedDecoder.kt @@ -0,0 +1,95 @@ +package world.gregs.voidps.engine.map.obj + +import world.gregs.voidps.buffer.read.BufferReader +import world.gregs.voidps.cache.Cache +import world.gregs.voidps.cache.Index +import world.gregs.voidps.cache.definition.decoder.MapObjectDecoder +import world.gregs.voidps.engine.data.definition.ObjectDefinitions +import world.gregs.voidps.engine.entity.obj.GameObjects +import world.gregs.voidps.type.Zone +import world.gregs.voidps.type.area.Rectangle + +/** + * Adds all objects except bridges from a single [zone], into a different zone, with [zoneRotation] applied. + */ +class MapObjectsRotatedDecoder( + private val objects: GameObjects, + private val definitions: ObjectDefinitions +) : MapObjectDecoder() { + + internal var zoneRotation: Int = 0 + internal lateinit var zone: Rectangle + + fun decode(cache: Cache, tiles: LongArray, from: Zone, to: Zone, rotation: Int, keys: IntArray?) { + val objectData = cache.data(Index.MAPS, "l${from.region.x}_${from.region.y}", xtea = keys) ?: return + val reader = BufferReader(objectData) + val x = from.tile.x.rem(64) + val y = from.tile.y.rem(64) + zone = Rectangle(x, y, x + 8, y + 8) + zoneRotation = rotation + super.decode(reader, tiles, to.tile.x, to.tile.y) + } + + override fun add(objectId: Int, localX: Int, localY: Int, level: Int, shape: Int, rotation: Int, regionTileX: Int, regionTileY: Int) { + if (objectId > definitions.definitions.size || !zone.contains(localX, localY)) { + return + } + val def = definitions.getValue(objectId) + val objRotation = (rotation + zoneRotation) and 0x3 + val rotX = rotateX(localX.rem(8), localY.rem(8), def.sizeX, def.sizeY, objRotation, zoneRotation) + val rotY = rotateY(localX.rem(8), localY.rem(8), def.sizeX, def.sizeY, objRotation, zoneRotation) + objects.set(objectId, regionTileX + rotX, regionTileY + rotY, level, shape, objRotation, def) + } + + companion object { + private fun rotateX( + objX: Int, + objY: Int, + sizeX: Int, + sizeY: Int, + objRotation: Int, + zoneRotation: Int + ): Int { + var x = sizeX + var y = sizeY + val rotation = zoneRotation and 0x3 + if (objRotation and 0x1 == 1) { + val temp = x + x = y + y = temp + } + if (rotation == 0) { + return objX + } + if (rotation == 1) { + return objY + } + return if (rotation == 2) 7 - objX - x + 1 else 7 - objY - y + 1 + } + + private fun rotateY( + objX: Int, + objY: Int, + sizeX: Int, + sizeY: Int, + objRotation: Int, + zoneRotation: Int + ): Int { + val rotation = zoneRotation and 0x3 + var x = sizeY + var y = sizeX + if (objRotation and 0x1 == 1) { + val temp = y + y = x + x = temp + } + if (rotation == 0) { + return objY + } + if (rotation == 1) { + return 7 - objX - y + 1 + } + return if (rotation == 2) 7 - objY - x + 1 else objX + } + } +} diff --git a/engine/src/test/kotlin/world/gregs/voidps/engine/entity/obj/GameObjectHashMapTest.kt b/engine/src/test/kotlin/world/gregs/voidps/engine/entity/obj/GameObjectHashMapTest.kt index 2142705ce1..732f581b66 100644 --- a/engine/src/test/kotlin/world/gregs/voidps/engine/entity/obj/GameObjectHashMapTest.kt +++ b/engine/src/test/kotlin/world/gregs/voidps/engine/entity/obj/GameObjectHashMapTest.kt @@ -3,8 +3,6 @@ package world.gregs.voidps.engine.entity.obj import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import world.gregs.voidps.type.Tile -import world.gregs.voidps.type.Zone class GameObjectHashMapTest { @@ -34,15 +32,4 @@ class GameObjectHashMapTest { val result = map.get(x = 2500, y = 3900, level = 1, layer = ObjectLayer.GROUND) assertEquals(GameObjects.value(true, obj.intId, obj.shape, obj.rotation), result) } - - @Test - fun `Set directly`() { - val obj = GameObject(1234, 2500, 3900, 1, 10, 2) - val value = GameObjects.value(false, obj.intId, obj.shape, obj.rotation) - val tileIndex = Tile.index(obj.x, obj.y, ObjectLayer.GROUND) - val zoneIndex = Zone.tileIndex(obj.x, obj.y, obj.level) - map[zoneIndex, tileIndex] = value - val result = map.get(x = 2500, y = 3900, level = 1, layer = ObjectLayer.GROUND) - assertEquals(value, result) - } } \ No newline at end of file diff --git a/engine/src/test/kotlin/world/gregs/voidps/engine/map/collision/CollisionDecoderTest.kt b/engine/src/test/kotlin/world/gregs/voidps/engine/map/collision/CollisionDecoderTest.kt new file mode 100644 index 0000000000..350f0cea24 --- /dev/null +++ b/engine/src/test/kotlin/world/gregs/voidps/engine/map/collision/CollisionDecoderTest.kt @@ -0,0 +1,96 @@ +package world.gregs.voidps.engine.map.collision + +import io.mockk.mockk +import io.mockk.spyk +import io.mockk.verify +import io.mockk.verifyOrder +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.rsmod.game.pathfinder.flag.CollisionFlag +import world.gregs.voidps.cache.definition.data.MapDefinition +import world.gregs.voidps.cache.definition.data.MapTile +import world.gregs.voidps.engine.map.collision.CollisionDecoder.Companion.BLOCKED_TILE +import world.gregs.voidps.engine.map.collision.CollisionDecoder.Companion.BRIDGE_TILE +import world.gregs.voidps.type.Region +import world.gregs.voidps.type.Zone + +internal class CollisionDecoderTest { + private lateinit var collisions: Collisions + private lateinit var decoder: CollisionDecoder + private lateinit var tiles: LongArray + + @BeforeEach + fun setup() { + collisions = mockk(relaxed = true) + decoder = spyk(CollisionDecoder(collisions)) + tiles = LongArray(64 * 64 * 4) + } + + @Test + fun `Load blocked`() { + // Given + val region = Region(1, 1) + tiles[MapDefinition.index(1, 1, 0)] = MapTile.pack(0, 0, 0, 0, 0, BLOCKED_TILE, 0) + // When + decoder.decode(tiles, region.tile.x, region.tile.y) + // Then + verifyOrder { + collisions.add(region.tile.x + 1, region.tile.y + 1, 0, CollisionFlag.FLOOR) + } + } + + @Test + fun `Ignore blocked bridge`() { + // Given + val region = Region(1, 1) + tiles[MapDefinition.index(1, 1, 0)] = MapTile.pack(0, 0, 0, 0, 0, BLOCKED_TILE, 0) + tiles[MapDefinition.index(1, 1, 1)] = MapTile.pack(0, 0, 0, 0, 0, BRIDGE_TILE, 0) + // When + decoder.decode(tiles, region.tile.x, region.tile.y) + // Then + verify(exactly = 0) { + collisions.add(any(), any(), any(), CollisionFlag.FLOOR) + } + } + + @Test + fun `Add suspended bridge`() { + // Given + val region = Region(1, 1) + tiles[MapDefinition.index(1, 1, 1)] = MapTile.pack(0, 0, 0, 0, 0, BLOCKED_TILE, 0) + tiles[MapDefinition.index(1, 1, 2)] = MapTile.pack(0, 0, 0, 0, 0, BRIDGE_TILE, 0) + // When + decoder.decode(tiles, region.tile.x, region.tile.y) + // Then + verify { + collisions.add(region.tile.x + 1, region.tile.y + 1, 1, CollisionFlag.FLOOR) + } + } + + @Test + fun `Load rotated and moved blocked`() { + // Given + val source = Zone(9, 9) + val target = Zone(18, 10) + tiles[MapDefinition.index(10, 12, 0)] = MapTile.pack(0, 0, 0, 0, 0, BLOCKED_TILE, 0) + // When + decoder.decode(tiles, source, target, 1) + // Then + verifyOrder { + collisions.add(target.tile.x + 4, target.tile.y + 5, 0, CollisionFlag.FLOOR) + } + } + + @Test + fun `Load rotated blocked`() { + // Given + val source = Zone(1, 1) + tiles[MapDefinition.index(10, 12, 0)] = MapTile.pack(0, 0, 0, 0, 0, BLOCKED_TILE, 0) + // When + decoder.decode(tiles, source, source, 1) + // Then + verifyOrder { + collisions.add(source.tile.x + 4, source.tile.y + 5, 0, CollisionFlag.FLOOR) + } + } +} \ No newline at end of file diff --git a/engine/src/test/kotlin/world/gregs/voidps/engine/map/collision/CollisionReaderTest.kt b/engine/src/test/kotlin/world/gregs/voidps/engine/map/collision/CollisionReaderTest.kt deleted file mode 100644 index cb3e2082b8..0000000000 --- a/engine/src/test/kotlin/world/gregs/voidps/engine/map/collision/CollisionReaderTest.kt +++ /dev/null @@ -1,70 +0,0 @@ -package world.gregs.voidps.engine.map.collision - -import io.mockk.mockk -import io.mockk.spyk -import io.mockk.verify -import io.mockk.verifyOrder -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.rsmod.game.pathfinder.flag.CollisionFlag -import world.gregs.voidps.cache.definition.data.MapDefinition -import world.gregs.voidps.cache.definition.data.MapTile -import world.gregs.voidps.engine.map.collision.CollisionReader.Companion.BLOCKED_TILE -import world.gregs.voidps.engine.map.collision.CollisionReader.Companion.BRIDGE_TILE -import world.gregs.voidps.type.Region - -internal class CollisionReaderTest { - lateinit var collisions: Collisions - lateinit var reader: CollisionReader - lateinit var map: MapDefinition - - @BeforeEach - fun setup() { - collisions = mockk(relaxed = true) - reader = spyk(CollisionReader(collisions)) - map = MapDefinition(id = 12345) - } - - @Test - fun `Load blocked`() { - // Given - val region = Region(1, 1) - map.setTile(1, 1, 0, MapTile(0, 0, 0, 0, 0, BLOCKED_TILE, 0)) - // When - reader.read(region, map) - // Then - verifyOrder { - map.getTile(1, 1, 0) - map.getTile(1, 1, 1) - collisions.add(region.tile.x + 1, region.tile.y + 1, 0, CollisionFlag.FLOOR) - } - } - - @Test - fun `Ignore blocked bridge`() { - // Given - val region = Region(1, 1) - map.setTile(1, 1, 0, MapTile(0, 0, 0, 0, 0, BLOCKED_TILE, 0)) - map.setTile(1, 1, 1, MapTile(0, 0, 0, 0, 0, BRIDGE_TILE, 0)) - // When - reader.read(region, map) - // Then - verify(exactly = 0) { - collisions.add(any(), any(), any(), CollisionFlag.FLOOR) - } - } - - @Test - fun `Add suspended bridge`() { - // Given - val region = Region(1, 1) - map.setTile(1, 1, 1, MapTile(0, 0, 0, 0, 0, BLOCKED_TILE, 0)) - map.setTile(1, 1, 2, MapTile(0, 0, 0, 0, 0, BRIDGE_TILE, 0)) - // When - reader.read(region, map) - // Then - verify { - collisions.add(region.tile.x + 1, region.tile.y + 1, 1, CollisionFlag.FLOOR) - } - } -} \ No newline at end of file diff --git a/engine/src/test/kotlin/world/gregs/voidps/engine/map/obj/MapObjectsDecoderTest.kt b/engine/src/test/kotlin/world/gregs/voidps/engine/map/obj/MapObjectsDecoderTest.kt new file mode 100644 index 0000000000..f8c9839fad --- /dev/null +++ b/engine/src/test/kotlin/world/gregs/voidps/engine/map/obj/MapObjectsDecoderTest.kt @@ -0,0 +1,113 @@ +package world.gregs.voidps.engine.map.obj + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import world.gregs.voidps.buffer.read.BufferReader +import world.gregs.voidps.buffer.write.BufferWriter +import world.gregs.voidps.cache.definition.data.ObjectDefinition +import world.gregs.voidps.engine.client.update.batch.ZoneBatchUpdates +import world.gregs.voidps.engine.data.definition.ObjectDefinitions +import world.gregs.voidps.engine.entity.obj.GameObjects +import world.gregs.voidps.engine.entity.obj.ObjectShape +import world.gregs.voidps.engine.map.collision.Collisions +import world.gregs.voidps.engine.map.collision.GameObjectCollision +import world.gregs.voidps.type.Tile + +class MapObjectsDecoderTest { + + private lateinit var definitions: ObjectDefinitions + private lateinit var objects: GameObjects + private lateinit var decoder: MapObjectsDecoder + private lateinit var tiles: LongArray + + @BeforeEach + fun setup() { + definitions = ObjectDefinitions(Array(10_000) { ObjectDefinition.EMPTY }) + objects = GameObjects(GameObjectCollision(Collisions()), ZoneBatchUpdates(), definitions, storeUnused = true) + decoder = MapObjectsDecoder(objects, definitions) + tiles = LongArray(64 * 64 * 4) + } + + @Test + fun `Load object`() { + val writer = BufferWriter() + writer.writeSmart(124) + writer.writeSmart(packTile(10, 11, 1)) + val shape = ObjectShape.GROUND_DECOR + writer.writeByte(packInfo(shape, 2)) + writer.writeSmart(0) + writer.writeSmart(0) + val reader = BufferReader(writer.toArray()) + decoder.decode(reader, tiles, 128, 256) + + val tile = Tile(138, 267, 1) + val gameObject = objects.getShape(tile, shape) + + assertNotNull(gameObject) + assertEquals(shape, gameObject!!.shape) + assertEquals(2, gameObject.rotation) + assertEquals(123, gameObject.intId) + } + + @Test + fun `Load multiple object locations with same id`() { + val writer = BufferWriter() + writer.writeSmart(124) + writer.writeSmart(packTile(10, 11, 1)) + writer.writeByte(packInfo(ObjectShape.WALL_CORNER, 0)) + writer.writeSmart(packTile(4, 4, 1)) + writer.writeByte(packInfo(ObjectShape.WALL_STRAIGHT, 1)) + writer.writeSmart(0) + writer.writeSmart(0) + val reader = BufferReader(writer.toArray()) + decoder.decode(reader, tiles, 128, 256) + + var tile = Tile(138, 267, 1) + var gameObject = objects.getShape(tile, ObjectShape.WALL_CORNER) + assertNotNull(gameObject) + assertEquals(0, gameObject!!.rotation) + assertEquals(123, gameObject.intId) + + tile = Tile(142, 271, 2) + gameObject = objects.getShape(tile, ObjectShape.WALL_STRAIGHT) + assertNotNull(gameObject) + assertEquals(1, gameObject!!.rotation) + assertEquals(123, gameObject.intId) + } + + @Test + fun `Load multiple objects of different ids`() { + val writer = BufferWriter() + writer.writeSmart(124) + writer.writeSmart(packTile(10, 11, 0)) + writer.writeByte(packInfo(ObjectShape.GROUND_DECOR, 3)) + writer.writeSmart(0) + writer.writeSmart(1234) + writer.writeSmart(packTile(4, 8, 2)) + writer.writeByte(packInfo(ObjectShape.ROOF_DIAGONAL, 0)) + writer.writeSmart(0) + writer.writeSmart(0) + val reader = BufferReader(writer.toArray()) + decoder.decode(reader, tiles, 192, 64) + + var tile = Tile(202, 75, 0) + var gameObject = objects.getShape(tile, ObjectShape.GROUND_DECOR) + assertNotNull(gameObject) + assertEquals(3, gameObject!!.rotation) + assertEquals(123, gameObject.intId) + + tile = Tile(196, 72, 2) + gameObject = objects.getShape(tile, ObjectShape.ROOF_DIAGONAL) + assertNotNull(gameObject) + assertEquals(0, gameObject!!.rotation) + assertEquals(1357, gameObject.intId) + } + + companion object { + private fun packInfo(shape: Int, rotation: Int) = rotation + (shape shl 2) + + private fun packTile(localX: Int, localY: Int, level: Int) = (localY and 0x3f) + (localX and 0x3f shl 6) + (level shl 12) + 1 + } +} \ No newline at end of file diff --git a/engine/src/test/kotlin/world/gregs/voidps/engine/map/obj/MapObjectsRotatedDecoderTest.kt b/engine/src/test/kotlin/world/gregs/voidps/engine/map/obj/MapObjectsRotatedDecoderTest.kt new file mode 100644 index 0000000000..9e72626cc5 --- /dev/null +++ b/engine/src/test/kotlin/world/gregs/voidps/engine/map/obj/MapObjectsRotatedDecoderTest.kt @@ -0,0 +1,106 @@ +package world.gregs.voidps.engine.map.obj + +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import world.gregs.voidps.buffer.read.BufferReader +import world.gregs.voidps.buffer.write.BufferWriter +import world.gregs.voidps.cache.definition.data.ObjectDefinition +import world.gregs.voidps.engine.client.update.batch.ZoneBatchUpdates +import world.gregs.voidps.engine.data.definition.ObjectDefinitions +import world.gregs.voidps.engine.entity.obj.GameObjects +import world.gregs.voidps.engine.entity.obj.ObjectShape +import world.gregs.voidps.engine.map.collision.Collisions +import world.gregs.voidps.engine.map.collision.GameObjectCollision +import world.gregs.voidps.type.Tile +import world.gregs.voidps.type.area.Rectangle + +class MapObjectsRotatedDecoderTest { + + private lateinit var definitions: ObjectDefinitions + private lateinit var objects: GameObjects + private lateinit var decoder: MapObjectsRotatedDecoder + private lateinit var tiles: LongArray + + @BeforeEach + fun setup() { + definitions = ObjectDefinitions(Array(10_000) { ObjectDefinition.EMPTY }) + objects = GameObjects(GameObjectCollision(Collisions()), ZoneBatchUpdates(), definitions, storeUnused = true) + decoder = MapObjectsRotatedDecoder(objects, definitions) + tiles = LongArray(64 * 64 * 4) + } + + @Test + fun `Load object in moved zone`() { + val writer = BufferWriter() + writer.writeSmart(124) + writer.writeSmart(packTile(10, 11, 1)) + val shape = ObjectShape.GROUND_DECOR + writer.writeByte(packInfo(shape, 2)) + writer.writeSmart(0) + writer.writeSmart(0) + val reader = BufferReader(writer.toArray()) + + decoder.zoneRotation = 2 + decoder.zone = Rectangle(8, 8, 16, 16) + decoder.decode(reader, tiles, 960, 896) + + val tile = Tile(965, 900, 1) // local 5, 4 + val gameObject = objects.getShape(tile, shape) + + assertNotNull(gameObject) + assertEquals(shape, gameObject!!.shape) + assertEquals(0, gameObject.rotation) + assertEquals(123, gameObject.intId) + } + + @Test + fun `Load object in rotated zone`() { + val writer = BufferWriter() + writer.writeSmart(124) + writer.writeSmart(packTile(2, 3, 0)) + val shape = ObjectShape.GROUND_DECOR + writer.writeByte(packInfo(shape, 2)) + writer.writeSmart(0) + writer.writeSmart(0) + val reader = BufferReader(writer.toArray()) + + decoder.zoneRotation = 3 + decoder.zone = Rectangle(0, 0, 8, 8) + decoder.decode(reader, tiles, 0, 0) + + val tile = Tile(4, 2, 0) + val gameObject = objects.getShape(tile, shape) + + assertNotNull(gameObject) + assertEquals(shape, gameObject!!.shape) + assertEquals(1, gameObject.rotation) + assertEquals(123, gameObject.intId) + } + + @Test + fun `Load ignores objects out of zone`() { + val writer = BufferWriter() + writer.writeSmart(124) + writer.writeSmart(packTile(18, 19, 0)) + val shape = ObjectShape.ROOF_EDGE_CORNER + writer.writeByte(packInfo(shape, 1)) + writer.writeSmart(0) + writer.writeSmart(0) + val reader = BufferReader(writer.toArray()) + + decoder.zoneRotation = 0 + decoder.zone = Rectangle(8, 8, 16, 16) + decoder.decode(reader, tiles, 64, 64) + + val tile = Tile(82, 84, 0) + val gameObject = objects.getShape(tile, shape) + assertNull(gameObject) + } + + companion object { + private fun packInfo(shape: Int, rotation: Int) = rotation + (shape shl 2) + + private fun packTile(localX: Int, localY: Int, level: Int) = (localY and 0x3f) + (localX and 0x3f shl 6) + (level shl 12) + 1 + } +} \ No newline at end of file diff --git a/file-server.properties b/file-server.properties index e54a5847ce..06f125986a 100644 --- a/file-server.properties +++ b/file-server.properties @@ -3,4 +3,4 @@ revision=634 cachePath=./data/cache/ rsaModulus=d6808be939bbfd2ec4e96b1581ce3e1144b526e7643a72e3c64fbb902724fbfcf14ab601da6d6f8dbb57d1c369d080d9fc392abeb7886e0076d07f2aea5810e540d2817fd1967e35b39cc95cf7c9170b5fb55f5bf95524b60e938f0d64614bc365b87d66963a8cc8664e32875366099ef297180d01c7c3842162865e11d92299 rsaPrivate=bd7a119cf43de5f90141fb30a5582ca58e5ec2bdd560780a522c2e4fb8f4478f790978db0c3a6d36f28d31a2ff7e89c384b46ed8c740c182b1719d53a86c2086f376d1c213785fd35c2aac5648195d10681d00a8c801dcebc1c7645daad5824c95430324a71228bb43be1bb7df6ac6ca8587f0848cf765fb850f40486b5475ed -prefetchKeys=104,79328,55571,46770,24563,299978,44375,0,4177,2822,99906,617909,155187,282646,330116,682557,18883,19031,16187,1248,6254,526,119,741135,821698,3671,2908 \ No newline at end of file +prefetchKeys=104,79328,55571,46770,24563,299978,44375,0,4177,2822,102323,618372,170616,332545,388299,705815,18893,22788,18115,1269,6254,532,119,756180,821696,3673,2908 \ No newline at end of file diff --git a/game/src/main/kotlin/world/gregs/voidps/Main.kt b/game/src/main/kotlin/world/gregs/voidps/Main.kt index d8f541427d..6f2eb000f7 100644 --- a/game/src/main/kotlin/world/gregs/voidps/Main.kt +++ b/game/src/main/kotlin/world/gregs/voidps/Main.kt @@ -8,9 +8,9 @@ import org.koin.dsl.module import org.koin.fileProperties import org.koin.logger.slf4jLogger import world.gregs.voidps.cache.Cache -import world.gregs.voidps.cache.CacheDelegate +import world.gregs.voidps.cache.FileCache import world.gregs.voidps.cache.Index -import world.gregs.voidps.cache.active.ActiveCache +import world.gregs.voidps.cache.MemoryCache import world.gregs.voidps.cache.config.decoder.InventoryDecoder import world.gregs.voidps.cache.config.decoder.StructDecoder import world.gregs.voidps.cache.definition.decoder.* @@ -29,7 +29,7 @@ import world.gregs.voidps.engine.entity.World import world.gregs.voidps.engine.entity.character.npc.NPCs import world.gregs.voidps.engine.entity.character.player.Players import world.gregs.voidps.engine.entity.item.floor.FloorItems -import world.gregs.voidps.engine.map.region.Xteas +import world.gregs.voidps.engine.map.collision.CollisionDecoder import world.gregs.voidps.network.Network import world.gregs.voidps.network.protocol import world.gregs.voidps.script.loadScripts @@ -44,19 +44,14 @@ object Main { lateinit var name: String private val logger = InlineLogger() - private const val USE_ACTIVE_CACHE = true + private const val USE_MEMORY_CACHE = false @OptIn(ExperimentalUnsignedTypes::class) @JvmStatic fun main(args: Array) { val startTime = System.currentTimeMillis() - val module = if (USE_ACTIVE_CACHE) { - val activeDir = File("./data/cache/active/") - ActiveCache().checkChanges(activeDir.parent, activeDir.name) - active(activeDir) - } else { - cache(CacheDelegate("./data/cache/")) - } + val module = cache((if (USE_MEMORY_CACHE) MemoryCache else FileCache).load("./data/cache/")) + logger.info { "Cache loaded in ${System.currentTimeMillis() - startTime}ms" } preload(module) name = getProperty("name") val revision = getProperty("revision").toInt() @@ -118,41 +113,22 @@ object Main { loadScripts(getProperty("scriptModule")) } - private fun active(activeDir: File) = module { - single(createdAtStart = true) { MapDefinitions(get(), get(), get()).load(activeDir) } - single(createdAtStart = true) { Huffman().load(activeDir.resolve(ActiveCache.indexFile(Index.HUFFMAN)).readBytes()) } - single(createdAtStart = true) { ObjectDefinitions(ObjectDecoder(member = getProperty("members") == "true", lowDetail = false, get()).load(activeDir)).load() } - single(createdAtStart = true) { NPCDefinitions(NPCDecoder(member = getProperty("members") == "true", get()).load(activeDir)).load() } - single(createdAtStart = true) { ItemDefinitions(ItemDecoder(get()).load(activeDir)).load() } - single(createdAtStart = true) { AnimationDefinitions(AnimationDecoder().load(activeDir)).load() } - single(createdAtStart = true) { EnumDefinitions(EnumDecoder().load(activeDir), get()).load() } - single(createdAtStart = true) { GraphicDefinitions(GraphicDecoder().load(activeDir)).load() } - single(createdAtStart = true) { InterfaceDefinitions(InterfaceDecoder().load(activeDir)).load() } - single(createdAtStart = true) { InventoryDefinitions(InventoryDecoder().load(activeDir)).load() } - single(createdAtStart = true) { StructDefinitions(StructDecoder(get()).load(activeDir)).load() } - single(createdAtStart = true) { QuickChatPhraseDefinitions(QuickChatPhraseDecoder().load(activeDir)).load() } - single(createdAtStart = true) { WeaponStyleDefinitions().load() } - single(createdAtStart = true) { AmmoDefinitions().load() } - single(createdAtStart = true) { ParameterDefinitions(CategoryDefinitions().load(), get()).load() } - single(createdAtStart = true) { FontDefinitions(FontDecoder().load(activeDir)).load() } - } - private fun cache(cache: Cache) = module { - single(createdAtStart = true) { MapDefinitions(get(), get(), get()).loadCache(cache, get()) } - single(createdAtStart = true) { Huffman().load(cache.getFile(Index.HUFFMAN, 1)!!) } - single(createdAtStart = true) { ObjectDefinitions(ObjectDecoder(member = getProperty("members") == "true", lowDetail = false, get()).loadCache(cache)).load() } - single(createdAtStart = true) { NPCDefinitions(NPCDecoder(member = getProperty("members") == "true", get()).loadCache(cache)).load() } - single(createdAtStart = true) { ItemDefinitions(ItemDecoder(get()).loadCache(cache)).load() } - single(createdAtStart = true) { AnimationDefinitions(AnimationDecoder().loadCache(cache)).load() } - single(createdAtStart = true) { EnumDefinitions(EnumDecoder().loadCache(cache), get()).load() } - single(createdAtStart = true) { GraphicDefinitions(GraphicDecoder().loadCache(cache)).load() } - single(createdAtStart = true) { InterfaceDefinitions(InterfaceDecoder().loadCache(cache)).load() } - single(createdAtStart = true) { InventoryDefinitions(InventoryDecoder().loadCache(cache)).load() } - single(createdAtStart = true) { StructDefinitions(StructDecoder(get()).loadCache(cache)).load() } - single(createdAtStart = true) { QuickChatPhraseDefinitions(QuickChatPhraseDecoder().loadCache(cache)).load() } + single(createdAtStart = true) { MapDefinitions(CollisionDecoder(get()), get(), get(), cache).loadCache() } + single(createdAtStart = true) { Huffman().load(cache.data(Index.HUFFMAN, 1)!!) } + single(createdAtStart = true) { ObjectDefinitions(ObjectDecoder(member = getProperty("members") == "true", lowDetail = false, get()).load(cache)).load() } + single(createdAtStart = true) { NPCDefinitions(NPCDecoder(member = getProperty("members") == "true", get()).load(cache)).load() } + single(createdAtStart = true) { ItemDefinitions(ItemDecoder(get()).load(cache)).load() } + single(createdAtStart = true) { AnimationDefinitions(AnimationDecoder().load(cache)).load() } + single(createdAtStart = true) { EnumDefinitions(EnumDecoder().load(cache), get()).load() } + single(createdAtStart = true) { GraphicDefinitions(GraphicDecoder().load(cache)).load() } + single(createdAtStart = true) { InterfaceDefinitions(InterfaceDecoder().load(cache)).load() } + single(createdAtStart = true) { InventoryDefinitions(InventoryDecoder().load(cache)).load() } + single(createdAtStart = true) { StructDefinitions(StructDecoder(get()).load(cache)).load() } + single(createdAtStart = true) { QuickChatPhraseDefinitions(QuickChatPhraseDecoder().load(cache)).load() } single(createdAtStart = true) { WeaponStyleDefinitions().load() } single(createdAtStart = true) { AmmoDefinitions().load() } single(createdAtStart = true) { ParameterDefinitions(CategoryDefinitions().load(), get()).load() } - single(createdAtStart = true) { FontDefinitions(FontDecoder().loadCache(cache)).load() } + single(createdAtStart = true) { FontDefinitions(FontDecoder().load(cache)).load() } } } \ No newline at end of file diff --git a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/Equipment.kt b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/Equipment.kt index 7f7abf222a..e3f8a5392d 100644 --- a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/Equipment.kt +++ b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/Equipment.kt @@ -120,7 +120,7 @@ object Equipment { fun fireResistantShield(shield: String) = shield == "elemental_shield" || shield == "mind_shield" || shield == "body_shield" || shield == "dragonfire_shield" - fun wearingMatchingArenaGear(player: Player): Boolean = isMatchingArenaSpell(player.spell, player.equipped(EquipSlot.Cape).id) + fun wearingMatchingArenaGear(player: Player, spell: String): Boolean = isMatchingArenaSpell(spell, player.equipped(EquipSlot.Cape).id) fun isMatchingArenaSpell(spell: String, cape: String): Boolean = isSaradomin(spell, cape) || isGuthix(spell, cape) || isZamorak(spell, cape) fun isSaradomin(spell: String, cape: String): Boolean = spell == "saradomin_strike" && cape == "saradomin_cape" fun isGuthix(spell: String, cape: String): Boolean = spell == "claws_of_guthix" && cape == "guthix_cape" diff --git a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/hit/Damage.kt b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/hit/Damage.kt index 5b5a786804..bedf0541ab 100644 --- a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/hit/Damage.kt +++ b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/hit/Damage.kt @@ -98,10 +98,10 @@ object Damage { /** * Applies modifiers to a [maximum] */ - fun modify(source: Character, target: Character, type: String, baseMaxHit: Int, weapon: Item, special: Boolean = false): Int { + fun modify(source: Character, target: Character, type: String, baseMaxHit: Int, weapon: Item, spell: String, special: Boolean = false): Int { var damage = baseMaxHit - damage = Spell.damageModifiers(source, type, weapon, damage) + damage = Spell.damageModifiers(source, type, weapon, spell, damage) damage = Bonus.slayerModifier(source, target, type, damage, damage = true) diff --git a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/hit/Hit.kt b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/hit/Hit.kt index 5b57680dfc..6ef28235e8 100644 --- a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/hit/Hit.kt +++ b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/hit/Hit.kt @@ -128,7 +128,7 @@ fun Character.hit( special: Boolean = (this as? Player)?.specialAttack ?: false, damage: Int = Damage.roll(this, target, type, weapon, spell) ): Int { - val actualDamage = Damage.modify(this, target, type, damage, weapon, special) + val actualDamage = Damage.modify(this, target, type, damage, weapon, spell, special) .coerceAtMost(target.levels.get(Skill.Constitution)) events.emit(CombatAttack(target, type, actualDamage, weapon, spell, special, TICKS.toClientTicks(delay))) target.strongQueue("hit", delay) { diff --git a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/player/combat/magic/spell/Spell.kt b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/player/combat/magic/spell/Spell.kt index a8b441cc90..27be51b3f5 100644 --- a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/player/combat/magic/spell/Spell.kt +++ b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/player/combat/magic/spell/Spell.kt @@ -47,7 +47,7 @@ object Spell { /** * Applies modifications to spells damage */ - fun damageModifiers(source: Character, type: String, weapon: Item, baseDamage: Int): Int { + fun damageModifiers(source: Character, type: String, weapon: Item, spell: String, baseDamage: Int): Int { if (type != "magic") { return baseDamage } @@ -59,7 +59,7 @@ object Spell { val damageMultiplier = 1.0 + equipmentDamage + eliteVoidDamage damage = (damage * damageMultiplier).roundToInt() } - if (source.hasClock("charge") && source is Player && Equipment.wearingMatchingArenaGear(source)) { + if (source.hasClock("charge") && source is Player && Equipment.wearingMatchingArenaGear(source, spell)) { damage += 100 } return damage diff --git a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/player/equip/EquipmentBonuses.kts b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/player/equip/EquipmentBonuses.kts index 1bfd9e2ec0..c9244d5aac 100644 --- a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/player/equip/EquipmentBonuses.kts +++ b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/player/equip/EquipmentBonuses.kts @@ -55,6 +55,13 @@ on({ it.equipping() && (id == "equipment_side" || id == "equipm showStats(player, definitions.get(item.id)) } +on({ it.equipping() && (id == "equipment_side" || id == "equipment_bonuses") && component == "stats_done" && option == "Done" }) { player: Player -> + player.clear("equipment_titles") + player.clear("equipment_names") + player.clear("equipment_stats") + player.clear("equipment_name") +} + /* Redirect equipping actions to regular inventories */ @@ -167,20 +174,25 @@ fun showStats(player: Player, item: ItemDefinition) { if (item.contains("attack_speed")) { val attackSpeed = item["attack_speed", 4] - appendLine("attack rate", when (attackSpeed) { - 2 -> "Very fast" - 3 -> "Fast" - 4 -> "Standard" - 5 -> "Slow" - 6 -> "Very slow" - else -> attackSpeed.toString() - }) + if (attackSpeed != 0) { + appendLine("Attack Rate", when (attackSpeed) { + 2 -> "Very fast" + 3 -> "Fast" + 4 -> "Standard" + 5 -> "Slow" + 6 -> "Very slow" + else -> attackSpeed.toString() + }) + } } appendLine("Weight", "${df.format(item["weight", 0.0])} kg") player["equipment_titles"] = titles.toString() player["equipment_names"] = types.toString() player["equipment_stats"] = stats.toString() + player.sendVariable("equipment_titles") + player.sendVariable("equipment_names") + player.sendVariable("equipment_stats") } val df = DecimalFormat("0.0").apply { diff --git a/game/src/main/kotlin/world/gregs/voidps/world/interact/world/map/RegionLoading.kts b/game/src/main/kotlin/world/gregs/voidps/world/interact/world/map/RegionLoading.kts index dd1ad65fd6..0ca95dabe0 100644 --- a/game/src/main/kotlin/world/gregs/voidps/world/interact/world/map/RegionLoading.kts +++ b/game/src/main/kotlin/world/gregs/voidps/world/interact/world/map/RegionLoading.kts @@ -13,13 +13,11 @@ import world.gregs.voidps.engine.event.Priority import world.gregs.voidps.engine.event.on import world.gregs.voidps.engine.inject import world.gregs.voidps.engine.map.region.RegionRetry -import world.gregs.voidps.engine.map.region.Xteas import world.gregs.voidps.engine.map.zone.DynamicZones import world.gregs.voidps.engine.map.zone.ReloadZone import world.gregs.voidps.network.encode.dynamicMapRegion import world.gregs.voidps.network.encode.mapRegion import world.gregs.voidps.type.Distance -import world.gregs.voidps.type.Region import world.gregs.voidps.type.Zone /** @@ -27,7 +25,6 @@ import world.gregs.voidps.type.Zone * Loads maps when they are accessed */ -val xteas: Xteas by inject() val players: Players by inject() val dynamicZones: DynamicZones by inject() @@ -119,7 +116,7 @@ fun update(player: Player, initial: Boolean, force: Boolean) { val radius = viewport.zoneRadius for (regionX in (zone.x - radius) / 8..(zone.x + radius) / 8) { for (regionY in (zone.y - radius) / 8..(zone.y + radius) / 8) { - val xtea = xteas[Region.id(regionX, regionY)] ?: blankXtea + val xtea = blankXtea xteaList.add(xtea) } } @@ -153,9 +150,8 @@ fun updateDynamic(player: Player, initial: Boolean, force: Boolean) { zones.add(null) continue } - val region = DynamicZones.getZone(target).region zones.add(target) - val xtea = xteas[region] ?: blankXtea + val xtea = blankXtea if (!xteaList.contains(xtea)) { xteaList.add(xtea) } else { diff --git a/game/src/main/kotlin/world/gregs/voidps/world/map/Examines.kts b/game/src/main/kotlin/world/gregs/voidps/world/map/Examines.kts index 8fa1383b03..456cf62f75 100644 --- a/game/src/main/kotlin/world/gregs/voidps/world/map/Examines.kts +++ b/game/src/main/kotlin/world/gregs/voidps/world/map/Examines.kts @@ -1,6 +1,7 @@ package world.gregs.voidps.world.map import world.gregs.voidps.engine.client.message +import world.gregs.voidps.engine.client.ui.InterfaceOption import world.gregs.voidps.engine.entity.character.npc.NPCOption import world.gregs.voidps.engine.entity.character.player.Player import world.gregs.voidps.engine.entity.character.player.chat.ChatType @@ -8,6 +9,10 @@ import world.gregs.voidps.engine.entity.obj.ObjectOption import world.gregs.voidps.engine.event.on import world.gregs.voidps.world.interact.entity.player.equip.InventoryOption +on({ id == "equipment_bonuses" && option == "Examine" }) { player: Player -> + player.message(item.def.getOrNull("examine") ?: return@on, ChatType.ItemExamine) +} + on({ option == "Examine" }) { player: Player -> player.message(item.def.getOrNull("examine") ?: return@on, ChatType.ItemExamine) } diff --git a/game/src/main/resources/game.properties b/game/src/main/resources/game.properties index 0f7dfa16bb..09c4eea629 100644 --- a/game/src/main/resources/game.properties +++ b/game/src/main/resources/game.properties @@ -35,7 +35,6 @@ categoryDefinitionsPath=./data/definitions/categories.yml parameterDefinitionsPath=./data/definitions/parameters.yml dropsPath=./data/spawns/drops.yml bookPath=./data/definitions/books.yml -xteaPath=./data/xteas.dat revision=634 loginLimit=5 scriptModule=.\\game\\src\\main\\kotlin\\ diff --git a/game/src/test/kotlin/world/gregs/voidps/world/interact/entity/player/combat/ChargeGodSpellEffectTest.kt b/game/src/test/kotlin/world/gregs/voidps/world/interact/entity/player/combat/ChargeGodSpellEffectTest.kt index 4cf563755b..6fc8c71179 100644 --- a/game/src/test/kotlin/world/gregs/voidps/world/interact/entity/player/combat/ChargeGodSpellEffectTest.kt +++ b/game/src/test/kotlin/world/gregs/voidps/world/interact/entity/player/combat/ChargeGodSpellEffectTest.kt @@ -42,15 +42,14 @@ internal class ChargeGodSpellEffectTest : CombatFormulaTest() { player.hit(target, type = "magic", spell = "saradomin_strike", damage = 100) tick(2) -// assertEquals(790, target.levels.get(Skill.Constitution)) -// TODO Waiting for hit roll order to change + assertEquals(790, target.levels.get(Skill.Constitution)) } @Test fun `Charge with god spell and different cape does nothing`() { val player = createPlayer(Skill.Magic to 99) - player.equipment.set(EquipSlot.Cape.index, "saradomin_cape") - player.equipment.set(EquipSlot.Weapon.index, "zamorak_staff") + player.equipment.set(EquipSlot.Cape.index, "zamorak_cape") + player.equipment.set(EquipSlot.Weapon.index, "saradomin_staff") player.inventory.add("fire_rune", 3) player.inventory.add("blood_rune", 3) player.inventory.add("air_rune", 3) diff --git a/game/src/test/kotlin/world/gregs/voidps/world/interact/entity/player/combat/CombatFormulaTest.kt b/game/src/test/kotlin/world/gregs/voidps/world/interact/entity/player/combat/CombatFormulaTest.kt index 3f6e9bb5a3..06acda9a72 100644 --- a/game/src/test/kotlin/world/gregs/voidps/world/interact/entity/player/combat/CombatFormulaTest.kt +++ b/game/src/test/kotlin/world/gregs/voidps/world/interact/entity/player/combat/CombatFormulaTest.kt @@ -22,7 +22,7 @@ abstract class CombatFormulaTest : WorldTest() { val offensiveRating = Hit.rating(source, target, type, weapon, special, true) val defensiveRating = Hit.rating(source, target, type, weapon, special, false) val maxHit = Damage.maximum(source, target, type, weapon, spell) - val actualMaxHit = Damage.modify(source, target, type, maxHit, weapon, special) + val actualMaxHit = Damage.modify(source, target, type, maxHit, weapon, spell, special) val chance = Hit.chance(source, target, type, weapon, special) return Results(offensiveRating, defensiveRating, actualMaxHit, chance) } diff --git a/game/src/test/kotlin/world/gregs/voidps/world/script/WorldTest.kt b/game/src/test/kotlin/world/gregs/voidps/world/script/WorldTest.kt index a1d80d3310..df7a13c352 100644 --- a/game/src/test/kotlin/world/gregs/voidps/world/script/WorldTest.kt +++ b/game/src/test/kotlin/world/gregs/voidps/world/script/WorldTest.kt @@ -12,9 +12,8 @@ import org.koin.fileProperties import org.koin.test.KoinTest import world.gregs.voidps.FakeRandom import world.gregs.voidps.cache.Cache -import world.gregs.voidps.cache.CacheDelegate import world.gregs.voidps.cache.Index -import world.gregs.voidps.cache.active.ActiveCache +import world.gregs.voidps.cache.MemoryCache import world.gregs.voidps.cache.config.decoder.InventoryDecoder import world.gregs.voidps.cache.config.decoder.StructDecoder import world.gregs.voidps.cache.definition.decoder.* @@ -41,9 +40,9 @@ import world.gregs.voidps.engine.entity.obj.GameObjects import world.gregs.voidps.engine.entity.obj.ObjectShape import world.gregs.voidps.engine.event.EventHandlerStore import world.gregs.voidps.engine.inv.Inventory +import world.gregs.voidps.engine.map.collision.CollisionDecoder import world.gregs.voidps.engine.map.collision.Collisions import world.gregs.voidps.engine.map.collision.GameObjectCollision -import world.gregs.voidps.engine.map.region.Xteas import world.gregs.voidps.gameModule import world.gregs.voidps.getTickStages import world.gregs.voidps.network.Client @@ -137,6 +136,7 @@ abstract class WorldTest : KoinTest { @BeforeAll fun beforeAll() { + stopKoin() startKoin { printLogger(Level.ERROR) fileProperties("/test.properties") @@ -159,7 +159,6 @@ abstract class WorldTest : KoinTest { single(createdAtStart = true) { fontDefinitions } single { ammoDefinitions } single { parameterDefinitions } - single { xteas } single { gameObjects } single { mapDefinitions } single { collisions } @@ -167,7 +166,7 @@ abstract class WorldTest : KoinTest { }) } loadScripts(getProperty("scriptModule")) - MapDefinitions(get(), get(), get()).load(active) + MapDefinitions(CollisionDecoder(get()), get(), get(), cache).loadCache() saves = File(getProperty("savePath")) saves?.mkdirs() store = get() @@ -231,32 +230,26 @@ abstract class WorldTest : KoinTest { } companion object { - private val active: File by lazy { - ActiveCache { ActiveCache.load(getProperty("xteaPath")) } - .checkChanges(getProperty("cachePath"), "active") - File("../data/cache/active/") - } - private val cache: Cache by lazy { CacheDelegate(getProperty("cachePath")) } - private val huffman: Huffman by lazy { Huffman().load(active.resolve(ActiveCache.indexFile(Index.HUFFMAN)).readBytes()) } + private val cache: Cache by lazy { MemoryCache(getProperty("cachePath")) } + private val huffman: Huffman by lazy { Huffman().load(cache.data(Index.HUFFMAN, 1)!!) } private val ammoDefinitions: AmmoDefinitions by lazy { AmmoDefinitions().load() } private val parameterDefinitions: ParameterDefinitions by lazy { ParameterDefinitions(CategoryDefinitions().load(), ammoDefinitions).load() } - private val objectDefinitions: ObjectDefinitions by lazy { ObjectDefinitions(ObjectDecoder(member = true, lowDetail = false, parameterDefinitions).load(active)).load() } - private val npcDefinitions: NPCDefinitions by lazy { NPCDefinitions(NPCDecoder(member = true, parameterDefinitions).load(active)).load() } - private val itemDefinitions: ItemDefinitions by lazy { ItemDefinitions(ItemDecoder(parameterDefinitions).load(active)).load() } - private val animationDefinitions: AnimationDefinitions by lazy { AnimationDefinitions(AnimationDecoder().load(active)).load() } - private val graphicDefinitions: GraphicDefinitions by lazy { GraphicDefinitions(GraphicDecoder().load(active)).load() } - private val interfaceDefinitions: InterfaceDefinitions by lazy { InterfaceDefinitions(InterfaceDecoder().load(active)).load() } - private val inventoryDefinitions: InventoryDefinitions by lazy { InventoryDefinitions(InventoryDecoder().load(active)).load() } - private val structDefinitions: StructDefinitions by lazy { StructDefinitions(StructDecoder(parameterDefinitions).load(active)).load() } - private val quickChatPhraseDefinitions: QuickChatPhraseDefinitions by lazy { QuickChatPhraseDefinitions(QuickChatPhraseDecoder().load(active)).load() } + private val objectDefinitions: ObjectDefinitions by lazy { ObjectDefinitions(ObjectDecoder(member = true, lowDetail = false, parameterDefinitions).load(cache)).load() } + private val npcDefinitions: NPCDefinitions by lazy { NPCDefinitions(NPCDecoder(member = true, parameterDefinitions).load(cache)).load() } + private val itemDefinitions: ItemDefinitions by lazy { ItemDefinitions(ItemDecoder(parameterDefinitions).load(cache)).load() } + private val animationDefinitions: AnimationDefinitions by lazy { AnimationDefinitions(AnimationDecoder().load(cache)).load() } + private val graphicDefinitions: GraphicDefinitions by lazy { GraphicDefinitions(GraphicDecoder().load(cache)).load() } + private val interfaceDefinitions: InterfaceDefinitions by lazy { InterfaceDefinitions(InterfaceDecoder().load(cache)).load() } + private val inventoryDefinitions: InventoryDefinitions by lazy { InventoryDefinitions(InventoryDecoder().load(cache)).load() } + private val structDefinitions: StructDefinitions by lazy { StructDefinitions(StructDecoder(parameterDefinitions).load(cache)).load() } + private val quickChatPhraseDefinitions: QuickChatPhraseDefinitions by lazy { QuickChatPhraseDefinitions(QuickChatPhraseDecoder().load(cache)).load() } private val weaponStyleDefinitions: WeaponStyleDefinitions by lazy { WeaponStyleDefinitions().load() } - private val enumDefinitions: EnumDefinitions by lazy { EnumDefinitions(EnumDecoder().load(active), structDefinitions).load() } + private val enumDefinitions: EnumDefinitions by lazy { EnumDefinitions(EnumDecoder().load(cache), structDefinitions).load() } private val collisions: Collisions by lazy { Collisions() } private val objectCollision: GameObjectCollision by lazy { GameObjectCollision(collisions) } - private val xteas: Xteas by lazy { Xteas().load() } private val gameObjects: GameObjects by lazy { GameObjects(objectCollision, ZoneBatchUpdates(), objectDefinitions, storeUnused = true) } - private val mapDefinitions: MapDefinitions by lazy { MapDefinitions(collisions, objectDefinitions, gameObjects).load(active) } - private val fontDefinitions: FontDefinitions by lazy { FontDefinitions(FontDecoder().load(active)).load() } + private val mapDefinitions: MapDefinitions by lazy { MapDefinitions(CollisionDecoder( collisions), objectDefinitions, gameObjects, cache).loadCache() } + private val fontDefinitions: FontDefinitions by lazy { FontDefinitions(FontDecoder().load(cache)).load() } val emptyTile = Tile(2655, 4640) } } \ No newline at end of file diff --git a/game/src/test/resources/test.properties b/game/src/test/resources/test.properties index d9b5d358f7..211eca1e0f 100644 --- a/game/src/test/resources/test.properties +++ b/game/src/test/resources/test.properties @@ -36,7 +36,6 @@ categoryDefinitionsPath=../data/definitions/categories.yml parameterDefinitionsPath=../data/definitions/parameters.yml dropsPath=../data/spawns/drops.yml bookPath=../data/definitions/books.yml -xteaPath=../data/xteas.dat revision=634 loginLimit=5 scriptModule=..\\game\\src\\main\\kotlin\\ diff --git a/gradle.properties b/gradle.properties index be23f044b9..d2ad79eb6b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ displeeCacheVersion = 6.8.1 fastUtilVersion = 8.5.11 jacksonVersion = 2.14.1 kotlinIoVersion = 0.1.16 -kotlinCoroutinesVersion = 1.7.0 +kotlinCoroutinesVersion = 1.7.3 koinVersion = 3.4.0 koinLogVersion = 3.4.0 logbackVersion = 1.4.5 diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/AnimationDefinitions.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/AnimationDefinitions.kt index 828f8940ba..e4c58e5ba7 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/AnimationDefinitions.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/AnimationDefinitions.kt @@ -8,7 +8,7 @@ object AnimationDefinitions { @JvmStatic fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = AnimationDecoderFull().loadCache(cache) + val decoder = AnimationDecoderFull().load(cache) loop@ for (i in decoder.indices) { val def = decoder.getOrNull(i) ?: continue if (def.aBoolean691) { diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/ClientScriptDefinitions.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/ClientScriptDefinitions.kt index b59d4cff08..dd7c14c00b 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/ClientScriptDefinitions.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/ClientScriptDefinitions.kt @@ -9,7 +9,7 @@ object ClientScriptDefinitions { @JvmStatic fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = ClientScriptDecoder(revision634 = true).loadCache(cache) + val decoder = ClientScriptDecoder().load(cache) for (i in decoder.indices) { // if (i != 1142) { // continue @@ -30,15 +30,15 @@ object ClientScriptDefinitions { } fun getScriptId(cache: Cache, id: Int, context: Int): Int { - var scriptId = cache.getArchiveId(Index.CLIENT_SCRIPTS, context or (id shl 10)) + var scriptId = cache.archiveId(Index.CLIENT_SCRIPTS, context or (id shl 10)) if (scriptId != -1) { return scriptId } - scriptId = cache.getArchiveId(Index.CLIENT_SCRIPTS, (65536 + id shl 10) or context) + scriptId = cache.archiveId(Index.CLIENT_SCRIPTS, (65536 + id shl 10) or context) if (scriptId != -1) { return scriptId } - scriptId = cache.getArchiveId(Index.CLIENT_SCRIPTS, context or 0x3fffc00) + scriptId = cache.archiveId(Index.CLIENT_SCRIPTS, context or 0x3fffc00) return scriptId } diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/DropTableDefinitions.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/DropTableDefinitions.kt index b73b59823b..5fc1f3f042 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/DropTableDefinitions.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/DropTableDefinitions.kt @@ -20,7 +20,7 @@ object DropTableDefinitions { fileProperties("/tool.properties") modules(module { single { CacheDelegate(getProperty("cachePath")) as Cache } - single { ItemDefinitions(ItemDecoder().loadCache(get())).load(Yaml()) } + single { ItemDefinitions(ItemDecoder().load(get())).load(Yaml()) } }) }.koin val decoder = DropTables().load(Yaml()) diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/EnumDefinitions.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/EnumDefinitions.kt index 1a8de270fc..31f5d507ca 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/EnumDefinitions.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/EnumDefinitions.kt @@ -9,7 +9,7 @@ object EnumDefinitions { @JvmStatic fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = EnumDecoder().loadCache(cache) + val decoder = EnumDecoder().load(cache) for (i in decoder.indices) { val def = decoder.getOrNull(i) ?: continue println("$i $def") diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/FontDefinitions.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/FontDefinitions.kt index 269bf5a1a4..78d6ce7936 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/FontDefinitions.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/FontDefinitions.kt @@ -10,8 +10,8 @@ object FontDefinitions { @JvmStatic fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) - val definitions = FontDecoder().loadCache(cache) - val font = definitions[cache.getArchiveId(FONT_METRICS, "q8_full")] + val definitions = FontDecoder().load(cache) + val font = definitions[cache.archiveId(FONT_METRICS, "q8_full")] println(font.textWidth("This is a string")) println(font.splitLines("Another 'archaeologist'. I'm not going to let you plunder my master's tomb you know.", 380)) } diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/GraphicDefinitions.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/GraphicDefinitions.kt index 7480476148..bd0ede983a 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/GraphicDefinitions.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/GraphicDefinitions.kt @@ -8,7 +8,7 @@ object GraphicDefinitions { @JvmStatic fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = GraphicDecoder().loadCache(cache) + val decoder = GraphicDecoder().load(cache) println(decoder[212]) for (i in decoder.indices) { val def = decoder.getOrNull(i) ?: continue diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/IdentityKitDefinitions.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/IdentityKitDefinitions.kt index 4f7eab81e8..4caae82205 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/IdentityKitDefinitions.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/IdentityKitDefinitions.kt @@ -8,7 +8,7 @@ object IdentityKitDefinitions { @JvmStatic fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = IdentityKitDecoder().loadCache(cache) + val decoder = IdentityKitDecoder().load(cache) println(decoder.lastIndex) for (i in decoder.indices) { val def = decoder.getOrNull(i) ?: continue diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/InterfaceDefinitions.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/InterfaceDefinitions.kt index 1a866fbf51..3ac78aa346 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/InterfaceDefinitions.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/InterfaceDefinitions.kt @@ -8,7 +8,7 @@ object InterfaceDefinitions { @JvmStatic fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = InterfaceDecoderFull().loadCache(cache) + val decoder = InterfaceDecoderFull().load(cache) for (i in decoder.indices) { val def = decoder.getOrNull(i) ?: continue for (comp in def.components ?: continue) { diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/InventoryDefinitions.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/InventoryDefinitions.kt index f736d96d3e..e4492231ea 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/InventoryDefinitions.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/InventoryDefinitions.kt @@ -8,7 +8,7 @@ object InventoryDefinitions { @JvmStatic fun main(args: Array) { val cache: Cache = CacheDelegate("${System.getProperty("user.home")}\\Downloads\\rs718_cache\\") - val decoder = InventoryDecoder().loadCache(cache) + val decoder = InventoryDecoder().load(cache) for (i in decoder.indices) { val def = decoder.getOrNull(i) ?: continue println(def) diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/ItemDefinitions.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/ItemDefinitions.kt index 2192f082ae..acdb7d0cdf 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/ItemDefinitions.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/ItemDefinitions.kt @@ -18,7 +18,7 @@ object ItemDefinitions { val categories = CategoryDefinitions().load(yaml, property("categoryDefinitionsPath")) val ammo = AmmoDefinitions().load(yaml, property("ammoDefinitionsPath")) val parameters = ParameterDefinitions(categories, ammo).load(yaml, property("parameterDefinitionsPath")) - val decoder = ItemDefinitions(ItemDecoder(parameters).loadCache(cache)).load(yaml, property("itemDefinitionsPath")) + val decoder = ItemDefinitions(ItemDecoder(parameters).load(cache)).load(yaml, property("itemDefinitionsPath")) for (i in decoder.definitions.indices) { val def = decoder.getOrNull(i) ?: continue if (def.contains("extra_equipment_option")) { diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/NPCDefinitions.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/NPCDefinitions.kt index 5069e067a3..64bbc7532c 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/NPCDefinitions.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/NPCDefinitions.kt @@ -17,7 +17,7 @@ object NPCDefinitions { val categories = CategoryDefinitions().load(yaml, property("categoryDefinitionsPath")) val ammo = AmmoDefinitions().load(yaml, property("ammoDefinitionsPath")) val parameters = ParameterDefinitions(categories, ammo).load(yaml, property("parameterDefinitionsPath")) - val definitions = NPCDecoder(true, parameters).loadCache(cache) + val definitions = NPCDecoder(true, parameters).load(cache) val decoder = NPCDefinitions(definitions).load(yaml, property("npcDefinitionsPath")) for (i in decoder.definitions.indices) { val def = decoder.getOrNull(i) ?: continue diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/ObjectDefinitions.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/ObjectDefinitions.kt index eb15eb14f9..0ae58ea78f 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/ObjectDefinitions.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/ObjectDefinitions.kt @@ -9,7 +9,7 @@ object ObjectDefinitions { @JvmStatic fun main(args: Array) { val cache = CacheDelegate("./data/cache") - val decoder = ObjectDecoderFull(false, true).loadCache(cache) + val decoder = ObjectDecoderFull(false, true).load(cache) for (def in decoder) { if(def.params?.containsKey(599) == true) { println("${def.id} ${def.name} ${def.params}") diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/OreIdentifier.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/OreIdentifier.kt index 0d49fbfa4d..f7383e3b66 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/OreIdentifier.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/OreIdentifier.kt @@ -9,7 +9,7 @@ object OreIdentifier { @JvmStatic fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = ObjectDecoderFull(members = false, lowDetail = false).loadCache(cache) + val decoder = ObjectDecoderFull(members = false, lowDetail = false).load(cache) val map = mapOf( 3184 to 1, 3183 to 2, diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/QuestDefinitions.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/QuestDefinitions.kt index 5ff676356e..8824a774e2 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/QuestDefinitions.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/QuestDefinitions.kt @@ -9,7 +9,7 @@ object QuestDefinitions { @JvmStatic fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = QuestDecoder().loadCache(cache) + val decoder = QuestDecoder().load(cache) for (i in decoder.indices) { val def = decoder.getOrNull(i) ?: continue if (def.extras != null) { diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/QuickChatDefinitions.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/QuickChatDefinitions.kt index 3143766af0..9225d7632f 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/QuickChatDefinitions.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/QuickChatDefinitions.kt @@ -13,7 +13,7 @@ object QuickChatDefinitions { fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) val options = QuickChatOptionDecoder() - val phrases = QuickChatPhraseDecoder().loadCache(cache) + val phrases = QuickChatPhraseDecoder().load(cache) val enums = EnumDecoder() val items = ItemDecoder() val data = BufferReader((0..32).map { 0.toByte() }.toByteArray()) diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/StructDefinitions.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/StructDefinitions.kt index 4b00331856..fcf8f2cd9f 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/StructDefinitions.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/StructDefinitions.kt @@ -10,7 +10,7 @@ object StructDefinitions { @JvmStatic fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = StructDecoder().loadCache(cache) + val decoder = StructDecoder().load(cache) val set = mutableSetOf() for (i in decoder.indices) { val def = decoder.getOrNull(i) ?: continue diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/VarBitDefinitions.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/VarBitDefinitions.kt index e4f7ddeafa..57ef43af67 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/VarBitDefinitions.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/VarBitDefinitions.kt @@ -9,8 +9,8 @@ object VarBitDefinitions { @JvmStatic fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = VarBitDecoder().loadCache(cache) - val varpDecoder = PlayerVariableParameterDecoder().loadCache(cache) + val decoder = VarBitDecoder().load(cache) + val varpDecoder = PlayerVariableParameterDecoder().load(cache) for (i in decoder.indices) { val def = decoder.getOrNull(i) ?: continue println(def) diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/WorldMapDefinitions.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/WorldMapDefinitions.kt index 6262179bfc..aecbcde288 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/WorldMapDefinitions.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/WorldMapDefinitions.kt @@ -8,7 +8,7 @@ object WorldMapDefinitions { @JvmStatic fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = WorldMapDetailsDecoder().loadCache(cache) + val decoder = WorldMapDetailsDecoder().load(cache) for (i in decoder.indices) { val def = decoder.getOrNull(i) ?: continue println(def) diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/WorldMapInfoDefinitions.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/WorldMapInfoDefinitions.kt index 6585630589..93538fa0ff 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/WorldMapInfoDefinitions.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/WorldMapInfoDefinitions.kt @@ -8,7 +8,7 @@ object WorldMapInfoDefinitions { @JvmStatic fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = WorldMapInfoDecoder().loadCache(cache) + val decoder = WorldMapInfoDecoder().load(cache) for (i in decoder.indices) { val def = decoder.getOrNull(i) ?: continue println(def) diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/CacheBuilder.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/CacheBuilder.kt new file mode 100644 index 0000000000..2d5e8333ad --- /dev/null +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/CacheBuilder.kt @@ -0,0 +1,101 @@ +package world.gregs.voidps.tools.cache + +import com.displee.cache.CacheLibrary +import world.gregs.voidps.cache.FileCache +import world.gregs.voidps.tools.convert.DefinitionsParameterConverter +import world.gregs.voidps.tools.convert.InventoryConverter +import world.gregs.voidps.tools.map.MapPacker +import java.io.File +import kotlin.system.exitProcess + +/** + * Automatically builds a cache from scratch with all modifications + */ +object CacheBuilder { + + @JvmStatic + fun main(args: Array) { + val target = File("./data/cache/") + + val cache727 = File("${System.getProperty("user.home")}/Downloads/727 cache with most xteas/") + if (!cache727.exists()) { + System.err.println("Unable to find '727 cache with most xteas' please download manually.") + return + } + + checkCacheOverride(target) + + val temp = File("./temp/cache/") + temp.mkdir() + val path = temp.resolve("build/") + if (!path.deleteRecursively()) { + System.err.println("Unable to delete temp cache.") + return + } + path.mkdirs() + + println("Finding original cache...") + val source = OpenRS2.downloadCache(temp.resolve("cache-634/"), 283) + source.copyRecursively(path) + val xteas = OpenRS2.getKeys(283) + + println("Finding extra caches to take maps from...") + // Take config data from other revisions + val other = OpenRS2.downloadCache(temp.resolve("cache-718/"), 302) + InventoryConverter.convert(path, other) + DefinitionsParameterConverter.convert(path, other) + + // Take maps from other revisions + val cache681 = OpenRS2.downloadCache(temp.resolve("cache-681/"), 280) + val xteas681 = OpenRS2.getKeys(280) + val cache537 = OpenRS2.downloadCache(temp.resolve("cache-537/"), 257) + MapPacker.pack634(path, xteas, cache727, Xteas(), cache681, xteas681, cache537) + + // Further improvements + val cache667 = OpenRS2.downloadCache(temp.resolve("cache-667/"), 1473) + val library = CacheLibrary(path.path) + RemoveXteas.remove(library, xteas) + RemoveBzip2.remove(library) + MoveCameraClientScript.convert(library, cache667) + println("Rebuilding cache.") + library.rebuild(target) + addEmptyIndexFiles(target, library.last()?.id ?: 0) + PrefetchKeyGeneration.print(library) + } + + private fun checkCacheOverride(path: File) { + val idx = path.resolve("${FileCache.CACHE_FILE_NAME}.dat2") + if (idx.exists()) { + println("Cache exists at '${path}' continuing will override.") + System.err.println("Continuing will delete the current cache. Are you sure?") + if (!readln().startsWith("y", ignoreCase = true)) { + println("Cancelled.") + return + } + } + if (!path.exists()) { + path.mkdirs() + return + } + for (file in path.listFiles() ?: return) { + if (!file.isFile || file.nameWithoutExtension != FileCache.CACHE_FILE_NAME) { + continue + } + if (!file.delete()) { + System.err.println("Unable to delete temp cache. Is it in use by another process?") + exitProcess(0) + } + } + } + + private fun addEmptyIndexFiles(target: File, lastIndex: Int) { + for (i in 0..lastIndex) { + val file = target.resolve("${FileCache.CACHE_FILE_NAME}.idx$i") + if (file.exists()) { + continue + } + println("Filling in blank index $i.") + file.createNewFile() + } + } +} \ No newline at end of file diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/DumpEnums.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/DumpEnums.kt index 5237903df2..e840f38815 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/DumpEnums.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/DumpEnums.kt @@ -10,7 +10,7 @@ object DumpEnums { @JvmStatic fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = EnumDecoder().loadCache(cache) + val decoder = EnumDecoder().load(cache) val builder = StringBuilder() for (i in decoder.indices) { val def = decoder.getOrNull(i) ?: continue diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/DumpMap.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/DumpMap.kt index 8dff8185eb..70ce1f34fb 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/DumpMap.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/DumpMap.kt @@ -3,7 +3,6 @@ package world.gregs.voidps.tools.cache import world.gregs.voidps.cache.Cache import world.gregs.voidps.cache.CacheDelegate import world.gregs.voidps.cache.Index -import world.gregs.voidps.engine.map.region.Xteas import world.gregs.voidps.tools.property import world.gregs.voidps.type.Region import java.io.File @@ -14,8 +13,8 @@ object DumpMap { val cache: Cache = CacheDelegate(property("cachePath")) val xteas: Xteas = Xteas().load() val region = Region(12341) - val tiles = cache.getFile(Index.MAPS, "m${region.x}_${region.y}")!! - val objects = cache.getFile(Index.MAPS, "l${region.x}_${region.y}", xteas[region])!! + val tiles = cache.data(Index.MAPS, "m${region.x}_${region.y}")!! + val objects = cache.data(Index.MAPS, "l${region.x}_${region.y}", xteas[region])!! println("${region.x}_${region.y}") File("region${region.id}_tiles.dat").writeBytes(tiles) File("region${region.id}_objects.dat").writeBytes(objects) diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/DumpSprites.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/DumpSprites.kt index 4ddec3c235..1ca78722fe 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/DumpSprites.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/DumpSprites.kt @@ -12,7 +12,7 @@ object DumpSprites { @JvmStatic fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = SpriteDecoder().loadCache(cache) + val decoder = SpriteDecoder().load(cache) println(decoder.lastIndex) File("./sprites/").mkdir() for (i in decoder.indices) { diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/DumpStructs.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/DumpStructs.kt index 90d3584880..13d06a0a2b 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/DumpStructs.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/DumpStructs.kt @@ -10,7 +10,7 @@ object DumpStructs { @JvmStatic fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = StructDecoder().loadCache(cache) + val decoder = StructDecoder().load(cache) val builder = StringBuilder() for (i in decoder.indices) { val def = decoder.getOrNull(i) ?: continue diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/DumpStyles.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/DumpStyles.kt index d27de2b60d..01ddc27ac0 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/DumpStyles.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/DumpStyles.kt @@ -3,10 +3,10 @@ package world.gregs.voidps.tools.cache import net.pearx.kasechange.toSnakeCase import world.gregs.voidps.cache.Cache import world.gregs.voidps.cache.CacheDelegate -import world.gregs.voidps.engine.data.config.WeaponStyleDefinition import world.gregs.voidps.cache.definition.data.ClientScriptDefinition import world.gregs.voidps.cache.definition.data.Instructions import world.gregs.voidps.cache.definition.decoder.ClientScriptDecoder +import world.gregs.voidps.engine.data.config.WeaponStyleDefinition import world.gregs.voidps.tools.property import java.io.File @@ -42,7 +42,7 @@ object DumpStyles { @JvmStatic fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = ClientScriptDecoder(revision634 = true).loadCache(cache) + val decoder = ClientScriptDecoder().load(cache) val clientScript = decoder[1142] load(clientScript) val builder = StringBuilder() diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/HashCodeChecker.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/HashCodeChecker.kt new file mode 100644 index 0000000000..b50934f729 --- /dev/null +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/HashCodeChecker.kt @@ -0,0 +1,53 @@ +package world.gregs.voidps.tools.cache + +import java.io.File + +object HashCodeChecker { + + private val matches = mutableMapOf() + private lateinit var known: Map + private val input = File("./temp/hashes/check.tsv") + private const val WRITE_CHANGES = true + + @JvmStatic + fun main(args: Array) { + val checkList = input.readLines() + .map { it.split("\t").last() } + + val file = File("./temp/hashes/hashes-modified.tsv") + known = file.readLines() + .associate { + val parts = it.split("\t") + val string = parts.getOrNull(4) + parts[3].toInt() to if (string.isNullOrBlank()) null else string + } + + for ((key, value) in known) { + if (!value.isNullOrBlank() && value.hashCode() != key) { + println("Invalid $key $value - ${value.hashCode()}") + } + } + + for (check in checkList) { + val hash = check.hashCode() + if (known.containsKey(hash) && known[hash] == null) { + println("Found $hash '$check'") + matches[hash] = check + } + } + println("Checked ${checkList.size} against ${known.size} found ${matches.size} matches.") + if (WRITE_CHANGES) { + file.writeText(file.readLines().joinToString("\n") { line -> + val parts = line.split("\t").toMutableList() + if (parts.getOrNull(4).isNullOrBlank()) { + val id = parts[3].toInt() + if (matches.containsKey(id)) { + parts[4] = matches.getValue(id) + } + } + parts.joinToString("\t") + }) + println("Changes written to file.") + } + } +} \ No newline at end of file diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/HashCodeMatcher.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/HashCodeMatcher.kt index 11210473b4..69f3606918 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/HashCodeMatcher.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/HashCodeMatcher.kt @@ -36,8 +36,8 @@ object HashCodeMatcher { } findInterfaces(keywords, depth = -1) - findSprites(keywords, depth = 3) - findScripts(keywords, depth = -1) + findSprites(keywords, depth = -1) + findScripts(keywords, depth = 2) } fun add(string: String): Boolean { @@ -228,4 +228,17 @@ object HashCodeMatcher { } } } + + private fun brute(prefix: String, target: Int, chars: List, limit: Int, depth: Int = 0) { + if (depth > limit) { + return + } + for (a in chars) { + val str = "$prefix${a}" + if (str.hashCode() == target) { + println("Found: $str") + } + brute(str, target, chars, limit, depth + 1) + } + } } \ No newline at end of file diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/HashCodeVerify.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/HashCodeVerify.kt new file mode 100644 index 0000000000..738382fafe --- /dev/null +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/HashCodeVerify.kt @@ -0,0 +1,37 @@ +package world.gregs.voidps.tools.cache + +import java.io.File + +object HashCodeVerify { + + @JvmStatic + fun main(args: Array) { + val file = File("./temp/hashes/hashes-modified.tsv") + val known = file.readLines() + .associate { + val parts = it.split("\t") + val string = parts.getOrNull(4) + parts[3].toInt() to if (string.isNullOrBlank()) null else string + } + + for ((key, value) in known) { + if (!value.isNullOrBlank() && value.hashCode() != key) { + println("Invalid $key $value - ${value.hashCode()}") + } + } + + file.readLines().map { it.split("\t") }.groupBy { it[0] }.forEach { (index, lines) -> + val found = lines.count { !it.getOrNull(4).isNullOrBlank() } + + println(""" + [tr] + [td]$index[/td] + [td][/td] + [td]$found[/td] + [td]${lines.size}[/td] + [td]${String.format("%.2f", found / lines.size.toDouble() * 100.0)}%[/td] + [/tr] + """.trimIndent()) + } + } +} \ No newline at end of file diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/MoveCameraClientScript.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/MoveCameraClientScript.kt index 4c15e36376..f58d233e2f 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/MoveCameraClientScript.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/MoveCameraClientScript.kt @@ -1,19 +1,96 @@ package world.gregs.voidps.tools.cache import com.displee.cache.CacheLibrary +import world.gregs.voidps.buffer.read.BufferReader +import world.gregs.voidps.buffer.write.BufferWriter import world.gregs.voidps.cache.Index +import world.gregs.voidps.cache.definition.data.ClientScriptDefinition +import world.gregs.voidps.cache.definition.data.InterfaceComponentDefinitionFull +import world.gregs.voidps.cache.definition.decoder.ClientScriptDecoder +import world.gregs.voidps.cache.definition.decoder.InterfaceDecoderFull +import world.gregs.voidps.cache.definition.encoder.ClientScriptEncoder +import world.gregs.voidps.cache.definition.encoder.InterfaceEncoder +import java.io.File object MoveCameraClientScript { - @JvmStatic - fun main(args: Array) { - val cache667 = CacheLibrary("./data/667/") - val cache634 = CacheLibrary("./data/cache/") + private val INTERFACES = mapOf( + 548 to 3, + 746 to 0 + ) + private const val SCRIPT_ID = 4731 + + fun convert(cache: CacheLibrary, other: File) { + val otherCache = CacheLibrary(other.path) + + // Decode script + val scriptDef = findMouseScript(otherCache) + val newScriptId = addScript(cache, scriptDef) + println("Add mouse move client script ${newScriptId}.") + + packInterfacesWithScript(cache, otherCache, newScriptId) + cache.update() + } + + private fun addScript(cache: CacheLibrary, scriptDef: ClientScriptDefinition): Int { + val index = cache.index(Index.CLIENT_SCRIPTS) + val scriptEncoder = ClientScriptEncoder() + val writer = BufferWriter(1024) + with(scriptEncoder) { + writer.encode(scriptDef) + } + return index.add(writer.toArray()).id + } - val index667 = cache667.index(Index.CLIENT_SCRIPTS) - val index634 = cache634.index(Index.CLIENT_SCRIPTS) + private fun packInterfacesWithScript(cache: CacheLibrary, otherCache: CacheLibrary, newScriptId: Int) { + val index = cache.index(Index.INTERFACES) + val interfaceDecoder = InterfaceDecoderFull() + val encoder = InterfaceEncoder() + for ((id, parent) in INTERFACES) { + for (file in otherCache.index(Index.INTERFACES).archive(id)!!.fileIds()) { + val data = otherCache.data(Index.INTERFACES, id, file) ?: continue + // Read interface definition + val definition = InterfaceComponentDefinitionFull() + with(interfaceDecoder) { + definition.read(BufferReader(data)) + } - index634.add(index667.archive(4731)) - cache634.update() - println("Done") + // Find component with script + val motionHandler = definition.mouseMotionHandler + if (motionHandler == null || motionHandler.firstOrNull() != SCRIPT_ID) { + continue + } + + val archive = index.archive(id)!! + // Update + val newId = archive.nextId() + definition.id = newId + definition.parent = parent + motionHandler[0] = newScriptId + // Write + val buffer = BufferWriter(1024) + with(encoder) { + buffer.encode(definition) + } + archive.add(buffer.toArray()) + println("Added new component $newId to interface $id.") + } + } + } + + private fun findMouseScript(otherCache: CacheLibrary): ClientScriptDefinition { + val scriptDecoder = ClientScriptDecoder(revision667 = true) + val scriptData = otherCache.data(Index.CLIENT_SCRIPTS, SCRIPT_ID)!! + val scriptDef = ClientScriptDefinition() + scriptDef.id = SCRIPT_ID + scriptDecoder.readLoop(scriptDef, BufferReader(scriptData)) + return scriptDef + } + + @JvmStatic + fun main(args: Array) { + val path718 = "./temp/cache/cache-667/" + val path = "./data/cache/" + val cache = CacheLibrary(path) + convert(cache, File(path718)) } } \ No newline at end of file diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/OpenRS2.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/OpenRS2.kt new file mode 100644 index 0000000000..7bbec83516 --- /dev/null +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/OpenRS2.kt @@ -0,0 +1,82 @@ +package world.gregs.voidps.tools.cache + +import org.jsoup.Jsoup +import java.io.BufferedInputStream +import java.io.File +import java.io.FileOutputStream +import java.net.URL +import java.util.zip.ZipInputStream + +object OpenRS2 { + + fun downloadKeys(directory: File, target: Int): File { + if (!directory.exists()) { + directory.mkdirs() + } + val file = directory.resolve("${target}-keys.json") + if (!file.exists()) { + val text = Jsoup.connect("https://archive.openrs2.org/caches/runescape/${target}/keys.json") + .ignoreContentType(true) + .get() + .body() + .ownText() + file.writeText(text) + } + return file + } + + fun getKeys(target: Int, directory: File = File("./temp/cache/xteas/")): Xteas { + val file = downloadKeys(directory, target) + return Xteas(Xteas.loadJson(file.readText(), value = "key").toMutableMap()) + } + + fun downloadCache(directory: File, number: Int): File { + if (!directory.exists()) { + directory.mkdirs() + } + val file = directory.resolve("main_file_cache.dat2") + if (!file.exists()) { + downloadZip("https://archive.openrs2.org/caches/runescape/${number}/disk.zip", directory.path) + } + return directory + } + + private fun downloadZip(url: String, destinationDirectory: String) { + val zipInputStream = ZipInputStream(BufferedInputStream(URL(url).openStream())) + var entry = zipInputStream.nextEntry + while (entry != null) { + if (!entry.isDirectory && entry.name.startsWith(ZIP_DIRECTORY)) { + val relativePath = entry.name.substring(ZIP_DIRECTORY.length) + val filePath = File(destinationDirectory, relativePath) + filePath.parentFile.mkdirs() + FileOutputStream(filePath).use { output -> + val buffer = ByteArray(1024) + var bytesRead: Int + var totalBytesRead = 0L + + while (true) { + bytesRead = zipInputStream.read(buffer) + if (bytesRead == -1) { + break + } + + output.write(buffer, 0, bytesRead) + totalBytesRead += bytesRead + printProgress(totalBytesRead) + } + } + } + + zipInputStream.closeEntry() + entry = zipInputStream.nextEntry + } + zipInputStream.close() + } + + private fun printProgress(totalBytesRead: Long) { + val totalMegabytes = totalBytesRead.toDouble() / (1024 * 1024) + print("\rTotal Downloaded: %.2f MB".format(totalMegabytes)) + } + + private const val ZIP_DIRECTORY = "cache" +} \ No newline at end of file diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/PrefetchKeyGeneration.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/PrefetchKeyGeneration.kt index ab87c6042d..f8a4de6307 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/PrefetchKeyGeneration.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/PrefetchKeyGeneration.kt @@ -8,32 +8,37 @@ object PrefetchKeyGeneration { @JvmStatic fun main(args: Array) { val cache = CacheLibrary("./data/cache/") - println(cache.archive(Index.DEFAULTS)) - println(cache.native("jaclib")) - println(cache.native("jaggl")) - println(cache.native("jagdx")) - println(cache.native("jagmisc")) - println(cache.native("sw3d")) - println(cache.native("hw3d")) - println(cache.native("jagtheora")) - println(cache.archive(Index.SHADERS)) - println(cache.archive(Index.TEXTURE_DEFINITIONS)) - println(cache.archive(Index.CONFIGS)) - println(cache.archive(Index.OBJECTS)) - println(cache.archive(Index.ENUMS)) - println(cache.archive(Index.NPCS)) - println(cache.archive(Index.ITEMS)) - println(cache.archive(Index.ANIMATIONS)) - println(cache.archive(Index.GRAPHICS)) - println(cache.archive(Index.VAR_BIT)) - println(cache.archive(Index.QUICK_CHAT_MESSAGES)) - println(cache.archive(Index.QUICK_CHAT_MENUS)) - println(cache.archive(Index.PARTICLES)) - println(cache.archive(Index.BILLBOARDS)) - println(cache.group(Index.HUFFMAN, "huffman")) - println(cache.archive(Index.INTERFACES)) - println(cache.archive(Index.CLIENT_SCRIPTS)) - println(cache.archive(Index.FONT_METRICS)) + print(cache) + } + + fun print(cache: CacheLibrary) { + print("prefetchKeys=") + print("${cache.archive(Index.DEFAULTS)},") + print("${cache.native("jaclib")},") + print("${cache.native("jaggl")},") + print("${cache.native("jagdx")},") + print("${cache.native("jagmisc")},") + print("${cache.native("sw3d")},") + print("${cache.native("hw3d")},") + print("${cache.native("jagtheora")},") + print("${cache.archive(Index.SHADERS)},") + print("${cache.archive(Index.TEXTURE_DEFINITIONS)},") + print("${cache.archive(Index.CONFIGS)},") + print("${cache.archive(Index.OBJECTS)},") + print("${cache.archive(Index.ENUMS)},") + print("${cache.archive(Index.NPCS)},") + print("${cache.archive(Index.ITEMS)},") + print("${cache.archive(Index.ANIMATIONS)},") + print("${cache.archive(Index.GRAPHICS)},") + print("${cache.archive(Index.VAR_BIT)},") + print("${cache.archive(Index.QUICK_CHAT_MESSAGES)},") + print("${cache.archive(Index.QUICK_CHAT_MENUS)},") + print("${cache.archive(Index.PARTICLES)},") + print("${cache.archive(Index.BILLBOARDS)},") + print("${cache.group(Index.HUFFMAN, "huffman")},") + print("${cache.archive(Index.INTERFACES)},") + print("${cache.archive(Index.CLIENT_SCRIPTS)},") + print("${cache.archive(Index.FONT_METRICS)},") println(cache.group(Index.WORLD_MAP, "details")) } diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/RemoveBzip2.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/RemoveBzip2.kt new file mode 100644 index 0000000000..69c6d2c0b0 --- /dev/null +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/RemoveBzip2.kt @@ -0,0 +1,41 @@ +package world.gregs.voidps.tools.cache + +import com.displee.cache.CacheLibrary +import com.displee.compress.CompressionType + +object RemoveBzip2 { + fun remove(lib: CacheLibrary) { + println("Removing slow compression...") + var indices = 0 + var archives = 0 + for (index in lib.indices()) { + if (index.version == 0) { + continue + } + if (index.compressionType == CompressionType.BZIP2) { + index.compressionType = CompressionType.GZIP + index.flag() + indices++ + } + for (archive in index.archives()) { + for (file in archive.files) { + lib.data(index.id, archive.id, file.key) + } + if (archive.compressionType == CompressionType.BZIP2) { + archive.compressionType = CompressionType.GZIP + archive.flag() + archives++ + } + } + } + lib.update() + println("Removed old compression from $archives archives and $indices indices.") + } + + @JvmStatic + fun main(args: Array) { + val path = "./data/cache/test/" + val lib = CacheLibrary(path) + remove(lib) + } +} \ No newline at end of file diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/cache/RemoveXteas.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/RemoveXteas.kt new file mode 100644 index 0000000000..f175210910 --- /dev/null +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/RemoveXteas.kt @@ -0,0 +1,38 @@ +package world.gregs.voidps.tools.cache + +import com.displee.cache.CacheLibrary +import world.gregs.voidps.cache.Index +import world.gregs.voidps.type.Region + +object RemoveXteas { + + fun remove(library: CacheLibrary, xteas: Xteas) { + println("Removing all xteas...") + val index = library.index(Index.MAPS) + val indexId = Index.MAPS + + var regions = 0 + for (regionX in 0 until 256) { + for (regionY in 0 until 256) { + val id = Region.id(regionX, regionY) + val keys = xteas[id] + val name = "l${regionX}_$regionY" + val data = library.data(indexId, name, 0, keys) ?: continue + library.put(indexId, name, data) + regions++ + } + } + index.flag() + library.update() + println("Removed xteas from $regions regions.") + } + + @JvmStatic + fun main(args: Array) { + val xteas = Xteas().load("./tools/src/main/resources/xteas.dat", Xteas.DEFAULT_KEY, Xteas.DEFAULT_VALUE) + val path = "./data/cache/test/" + val lib = CacheLibrary(path) + + remove(lib, xteas) + } +} \ No newline at end of file diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/map/region/Xteas.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/Xteas.kt similarity index 98% rename from engine/src/main/kotlin/world/gregs/voidps/engine/map/region/Xteas.kt rename to tools/src/main/kotlin/world/gregs/voidps/tools/cache/Xteas.kt index 2bc78b2524..322c9cb836 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/map/region/Xteas.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/cache/Xteas.kt @@ -1,4 +1,4 @@ -package world.gregs.voidps.engine.map.region +package world.gregs.voidps.tools.cache import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap import world.gregs.voidps.buffer.read.BufferReader diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/convert/DefinitionsParameterConverter.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/convert/DefinitionsParameterConverter.kt index 390e116586..d9ef696af1 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/convert/DefinitionsParameterConverter.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/convert/DefinitionsParameterConverter.kt @@ -1,8 +1,5 @@ package world.gregs.voidps.tools.convert -import org.koin.core.context.startKoin -import org.koin.dsl.module -import org.koin.fileProperties import world.gregs.voidps.buffer.write.BufferWriter import world.gregs.voidps.cache.* import world.gregs.voidps.cache.Index.ITEMS @@ -15,50 +12,36 @@ import world.gregs.voidps.cache.definition.decoder.ObjectDecoderFull import world.gregs.voidps.cache.definition.encoder.ItemEncoder import world.gregs.voidps.cache.definition.encoder.NPCEncoder import world.gregs.voidps.cache.definition.encoder.ObjectEncoder +import java.io.File object DefinitionsParameterConverter { - @Suppress("USELESS_CAST") - @JvmStatic - fun main(args: Array) { - val cache667 = module { - single { CacheDelegate("${System.getProperty("user.home")}/Downloads/rs634_cache/") as Cache } - } - val cache718 = module { - single { CacheDelegate("${System.getProperty("user.home")}/Downloads/rs718_cache/") as Cache } - } - - val koin = startKoin { - fileProperties("/tool.properties") - modules(cache718) - }.koin - val itemDefinitions718 = ItemDecoder718().loadCache(koin.get()) - val npcDefinitions718 = NPCDecoder718().loadCache(koin.get()) - val objectDefinitions718 = ObjectDecoder718().loadCache(koin.get()) + fun convert(target: File, other: File) { + val cache = CacheDelegate(target.path) + val otherCache = CacheDelegate(other.path) - koin.unloadModules(listOf(cache718)) - koin.loadModules(listOf(cache667)) - - val cache = koin.get() as CacheDelegate + val itemDefinitions718 = ItemDecoder718().load(otherCache) + val npcDefinitions718 = NPCDecoder718().load(otherCache) + val objectDefinitions718 = ObjectDecoder718().load(otherCache) val itemDecoder = ItemDecoderFull() - val itemDefinitions = itemDecoder.loadCache(koin.get()) + val itemDefinitions = itemDecoder.load(cache) val itemEncoder = ItemEncoder() val itemCount = definition(itemDecoder, itemEncoder, itemDefinitions, itemDefinitions, itemDefinitions718, cache, ITEMS) println("Parameters transferred from $itemCount item definitions.") val npcDecoder = NPCDecoderFull(members = false) - val npcDefinitions = npcDecoder.loadCache(koin.get()) + val npcDefinitions = npcDecoder.load(cache) val npcDecoderMembers = NPCDecoderFull(members = true) - val npcDefinitionsMembers = npcDecoderMembers.loadCache(koin.get()) + val npcDefinitionsMembers = npcDecoderMembers.load(cache) val npcEncoder = NPCEncoder() val npcCount = definition(npcDecoder, npcEncoder, npcDefinitions, npcDefinitionsMembers, npcDefinitions718, cache, NPCS) println("Parameters transferred from $npcCount npc definitions.") - val objectDecoder = ObjectDecoderFull() - val objectDefinitions = objectDecoder.loadCache(koin.get()) - val objectDecoderMembers = ObjectDecoderFull() - val objectDefinitionsMembers = objectDecoderMembers.loadCache(koin.get()) + val objectDecoder = ObjectDecoderFull(members = false) + val objectDefinitions = objectDecoder.load(cache) + val objectDecoderMembers = ObjectDecoderFull(members = true) + val objectDefinitionsMembers = objectDecoderMembers.load(cache) val objectEncoder = ObjectEncoder() val objectCount = definition(objectDecoder, objectEncoder, objectDefinitions, objectDefinitionsMembers, objectDefinitions718, cache, OBJECTS) println("Parameters transferred from $objectCount object definitions.") @@ -104,4 +87,11 @@ object DefinitionsParameterConverter { } return count } + + @JvmStatic + fun main(args: Array) { + val cache = File("${System.getProperty("user.home")}/Downloads/rs634_cache/") + val other = File("${System.getProperty("user.home")}/Downloads/rs718_cache/") + convert(cache, other) + } } \ No newline at end of file diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/convert/InterfaceDecoder718.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/convert/InterfaceDecoder718.kt new file mode 100644 index 0000000000..188f12ce76 --- /dev/null +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/convert/InterfaceDecoder718.kt @@ -0,0 +1,254 @@ +package world.gregs.voidps.tools.convert + +import world.gregs.voidps.buffer.read.Reader +import world.gregs.voidps.cache.definition.data.InterfaceComponentDefinitionFull + +class InterfaceDecoder718 { + + fun InterfaceComponentDefinitionFull.read(reader: Reader) { + var flag0: Int = reader.readUnsignedByte() + if (flag0 == 255) { + flag0 = -1 + } + type = reader.readUnsignedByte() + if (type and 0x80 != 0) { + type = type and 0x7f + unknown = reader.readString() + } + contentType = reader.readUnsignedShort() + basePositionX = reader.readShort() + basePositionY = reader.readShort() + baseWidth = reader.readUnsignedShort() + baseHeight = reader.readUnsignedShort() + horizontalSizeMode = reader.readByte().toByte() + verticalSizeMode = reader.readByte().toByte() + horizontalPositionMode = reader.readByte().toByte() + verticalPositionMode = reader.readByte().toByte() + parent = reader.readUnsignedShort() + if (parent == 65535) { + parent = -1 + } + val flag1: Int = reader.readUnsignedByte() + hidden = flag1 and 0x1 != 0 + if (flag0 >= 0) { + disableHover = flag1 and 0x2 != 0 + } + if (type == 0) { + scrollWidth = reader.readUnsignedShort() + scrollHeight = reader.readUnsignedShort() + if (flag0 < 0) { + disableHover = reader.readUnsignedByte() == 1 + } + } + if (type == 5) { + defaultImage = reader.readInt() + imageRotation = reader.readUnsignedShort() + val flag3: Int = reader.readUnsignedByte() + val aBoolean1196 = flag3 and 0x1 != 0 + imageRepeat = flag3 and 0x2 != 0 + alpha = reader.readUnsignedByte() + rotation = reader.readUnsignedByte() + backgroundColour = reader.readInt() + flipVertical = reader.readUnsignedByte() == 1 + flipHorizontal = reader.readUnsignedByte() == 1 + colour = reader.readInt() + if (flag0 >= 3) { + val aBoolean1183 = reader.readUnsignedByte() == 1 + } + } + if (type == 6) { + defaultMediaType = 1 + defaultMediaId = reader.readBigSmart() + val i_4_: Int = reader.readUnsignedByte() + val bool = i_4_ and 0x1 == 1 + centreType = i_4_ and 0x2 == 2 + animated = i_4_ and 0x4 == 4 + ignoreZBuffer = i_4_ and 0x8 == 8 + if (bool) { + viewportX = reader.readShort() + viewportY = reader.readShort() + spritePitch = reader.readUnsignedShort() + spriteRoll = reader.readUnsignedShort() + spriteYaw = reader.readUnsignedShort() + spriteScale = reader.readUnsignedShort() + } else if (centreType) { + viewportX = reader.readShort() + viewportY = reader.readShort() + viewportZ = reader.readShort() + spritePitch = reader.readUnsignedShort() + spriteRoll = reader.readUnsignedShort() + spriteYaw = reader.readUnsignedShort() + spriteScale = reader.readShort() + } + animation = reader.readBigSmart() + if (horizontalSizeMode.toInt() != 0) viewportWidth = reader.readUnsignedShort() + if (verticalSizeMode.toInt() != 0) viewportHeight = reader.readUnsignedShort() + } + if (type == 4) { + fontId = reader.readBigSmart() + if (flag0 >= 2) { + val aBoolean1211 = reader.readUnsignedByte() == 1 + } + text = reader.readString() + lineHeight = reader.readUnsignedByte() + horizontalTextAlign = reader.readUnsignedByte() + verticalTextAlign = reader.readUnsignedByte() + shaded = reader.readUnsignedByte() == 1 + colour = reader.readInt() + alpha = reader.readUnsignedByte() + if (flag0 >= 0) { + val anInt1217 = reader.readUnsignedByte() + } + } + if (type == 3) { + colour = reader.readInt() + filled = reader.readUnsignedByte() == 1 + alpha = reader.readUnsignedByte() + } + if (type == 9) { + lineWidth = reader.readUnsignedByte() + colour = reader.readInt() + lineMirrored = reader.readUnsignedByte() == 1 + } + val setting: Int = reader.readUnsignedMedium() + var modifier: Int = reader.readUnsignedByte() + if (modifier != 0) { + keyRepeats = ByteArray(11) + keyCodes = ByteArray(11) + keyModifiers = IntArray(11) + while (modifier != 0) { + val index = (modifier shr 4) - 1 + modifier = modifier shl 8 or reader.readUnsignedByte() + modifier = modifier and 0xfff + if (modifier == 4095) { + modifier = -1 + } + val repeat: Byte = reader.readByte().toByte() + if (repeat.toInt() != 0) { + val aBoolean1220 = true + } + val code: Byte = reader.readByte().toByte() + keyModifiers!![index] = modifier + keyRepeats!![index] = repeat + keyCodes!![index] = code + modifier = reader.readUnsignedByte() + } + } + name = reader.readString() + val flag4: Int = reader.readUnsignedByte() + val optionCount = flag4 and 0xf + val iconCount = flag4 shr 4 + if (optionCount > 0) { + options = Array(optionCount) { + reader.readString() + } + } + if (iconCount > 0) { + val i_14_: Int = reader.readUnsignedByte() + mouseIcon = IntArray(i_14_ + 1) + for (i_15_ in mouseIcon!!.indices) mouseIcon!![i_15_] = -1 + mouseIcon!![i_14_] = reader.readUnsignedShort() + } + if (iconCount > 1) { + val i_16_: Int = reader.readUnsignedByte() + mouseIcon!![i_16_] = reader.readUnsignedShort() + } + optionOverride = reader.readString() + if (optionOverride == "") optionOverride = null + anInt4708 = reader.readUnsignedByte() + anInt4795 = reader.readUnsignedByte() + anInt4860 = reader.readUnsignedByte() + useOption = reader.readString() + var i_17_ = -1 + if (setting shr 11 and 0x7f != 0) { + i_17_ = reader.readUnsignedShort() + if (i_17_ == 65535) { + i_17_ = -1 + } + anInt4698 = reader.readUnsignedShort() + if (anInt4698 == 65535) { + anInt4698 = -1 + } + anInt4839 = reader.readUnsignedShort() + if (anInt4839 == 65535) { + anInt4839 = -1 + } + } + if (flag0 >= 0) { + var anInt1272 = reader.readUnsignedShort() + if (anInt1272 == 65535) { + anInt1272 = -1 + } + } +// aClass298_Sub38_1219 = Class298_Sub38(i_5_, i_17_) + if (flag0 >= 0) { + val i_18_: Int = reader.readUnsignedByte() + for (i_19_ in 0 until i_18_) { + val i_20_: Int = reader.readUnsignedMedium() + val i_21_: Int = reader.readInt() +// this.aClass437_1279.method5817(Class298_Sub35(i_21_), i_20_.toLong()) + } + val i_22_: Int = reader.readUnsignedByte() + for (i_23_ in 0 until i_22_) { + val i_24_: Int = reader.readUnsignedMedium() + val string: String = reader.readString() +// this.aClass437_1279.method5817(string, i_24_.toLong()) + } + } + anObjectArray4758 = decodeScript(reader) + mouseEnterHandler = decodeScript(reader) + mouseExitHandler = decodeScript(reader) + anObjectArray4771 = decodeScript(reader) + anObjectArray4768 = decodeScript(reader) + stateChangeHandler = decodeScript(reader) + invUpdateHandler = decodeScript(reader) + refreshHandler = decodeScript(reader) + updateHandler = decodeScript(reader) + anObjectArray4770 = decodeScript(reader) + if (flag0 >= 0) { + val anObjectArray1247 = decodeScript(reader) + } + mouseMotionHandler = decodeScript(reader) + mousePressedHandler = decodeScript(reader) + mouseDraggedHandler = decodeScript(reader) + mouseReleasedHandler = decodeScript(reader) + mouseDragPassHandler = decodeScript(reader) + anObjectArray4852 = decodeScript(reader) + anObjectArray4711 = decodeScript(reader) + anObjectArray4753 = decodeScript(reader) + anObjectArray4688 = decodeScript(reader) + anObjectArray4775 = decodeScript(reader) + clientVarp = decodeIntArray(reader) + inventories = decodeIntArray(reader) + anIntArray4789 = decodeIntArray(reader) + clientVarc = decodeIntArray(reader) + anIntArray4805 = decodeIntArray(reader) + } + + private fun InterfaceComponentDefinitionFull.decodeScript(buffer: Reader): Array? { + val length = if (buffer.remaining > 0) buffer.readUnsignedByte() else 0 + if (length == 0) { + return null + } + val objects = Array(length) { + val string = buffer.readUnsignedBoolean() + if (string) buffer.readString() else buffer.readInt() + } + hasScript = true + return objects + } + + private fun decodeIntArray(reader: Reader): IntArray? { + return try { + val length: Int = if (reader.remaining <= 0) 0 else reader.readUnsignedByte() + if (0 == length) return null + val array = IntArray(length) + for (i in 0 until length) array[i] = reader.readInt() + array + } catch (exception: RuntimeException) { + exception.printStackTrace() + null + } + } +} + diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/convert/InventoryConverter.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/convert/InventoryConverter.kt index f14b3e84c3..dbfda1acd2 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/convert/InventoryConverter.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/convert/InventoryConverter.kt @@ -1,9 +1,6 @@ package world.gregs.voidps.tools.convert -import org.koin.core.context.startKoin -import org.koin.dsl.module import world.gregs.voidps.buffer.write.BufferWriter -import world.gregs.voidps.cache.Cache import world.gregs.voidps.cache.CacheDelegate import world.gregs.voidps.cache.Config.INVENTORIES import world.gregs.voidps.cache.Index @@ -11,93 +8,86 @@ import world.gregs.voidps.cache.config.decoder.InventoryDecoder import world.gregs.voidps.cache.config.encoder.InventoryEncoder import world.gregs.voidps.cache.definition.decoder.ItemDecoder import world.gregs.voidps.engine.data.definition.ItemDefinitions -import world.gregs.voidps.engine.get import world.gregs.voidps.tools.property import world.gregs.yaml.Yaml +import java.io.File +import kotlin.collections.component1 +import kotlin.collections.component2 +import kotlin.collections.set /** * Converts inventories from one cache into another, dumping the default values into inventories.yml */ object InventoryConverter { - @Suppress("USELESS_CAST") - @JvmStatic - fun main(args: Array) { - val cacheModule = module { - single { CacheDelegate("${System.getProperty("user.home")}/Downloads/rs634_cache/") as Cache } - } - val cache718Module = module { - single { CacheDelegate("${System.getProperty("user.home")}/Downloads/rs718_cache/") as Cache } - } - val koin = startKoin { - }.koin - koin.loadModules(listOf(cache718Module)) - var decoder = InventoryDecoder().loadCache(get()) - val inventories = decoder.indices.associateWith { decoder.getOrNull(it) } - - koin.unloadModules(listOf(cache718Module)) - koin.loadModules(listOf(cacheModule)) - val encoder = InventoryEncoder() - val cache: Cache = get() + @Suppress("USELESS_CAST") + fun convert(target: File, provider: File) { + val targetCache = CacheDelegate(target.path) + val otherCache = CacheDelegate(provider.path) val yaml = Yaml() + val otherDecoder = InventoryDecoder().load(otherCache) + val targetDecoder = InventoryDecoder().load(targetCache) + val itemDefinitions = ItemDefinitions(ItemDecoder().load(targetCache)).load(yaml, property("itemDefinitionsPath")) + val encoder = InventoryEncoder() val data: MutableMap = yaml.load>(property("inventoryDefinitionsPath")).toMutableMap() - - val itemDecoder = ItemDefinitions(ItemDecoder().loadCache(cache)).load(Yaml(), property("itemDefinitionsPath")) - decoder = InventoryDecoder().loadCache(cache) var counter = 0 - for (i in decoder.indices) { - val def = decoder.getOrNull(i) - val cont = inventories[i] - if (def == null || cont == null) { + for (index in targetDecoder.indices) { + val otherDef = otherDecoder.getOrNull(index) + val targetDef = targetDecoder.getOrNull(index) + if (targetDef == null || otherDef == null) { continue } - if (def.length != cont.length) { -// println("Length changed $i ${def.length} ${cont.length} ${cont.ids?.mapIndexed { index, it -> "${itemDecoder.getOrNull(it)?.name} ${cont.amounts!![index]}" }?.joinToString(separator = ", ")}") - } - - if (cont.ids != null) { - def.ids = cont.ids?.filter { itemDecoder.getOrNull(it) != null }?.toIntArray() - def.amounts = cont.amounts!!.take(def.ids!!.size).toIntArray() - def.length = def.ids!!.size + if (otherDef.ids != null) { + targetDef.ids = otherDef.ids?.filter { itemDefinitions.getOrNull(it) != null }?.toIntArray() + targetDef.amounts = otherDef.amounts!!.take(targetDef.ids!!.size).toIntArray() + targetDef.length = targetDef.ids!!.size counter++ val writer = BufferWriter(4096) with(encoder) { - writer.encode(def) + writer.encode(targetDef) } - cache.write(Index.CONFIGS, INVENTORIES, i, writer.toArray()) + targetCache.write(Index.CONFIGS, INVENTORIES, index, writer.toArray()) var found: String? = null var int = false data.forEach { (key, value) -> - if (value is Int && value == i) { + if (value is Int && value == index) { found = key int = true - } else if (value is Map<*, *> && value["id"] as Int == i) { + } else if (value is Map<*, *> && value["id"] as Int == index) { found = key } } val list = mutableListOf>() - def.ids!!.forEachIndexed { index, id -> - list.add(mapOf(itemDecoder.get(id).stringId to def.amounts!![index])) + targetDef.ids!!.forEachIndexed { index, id -> + list.add(mapOf(itemDefinitions.get(id).stringId to targetDef.amounts!![index])) } if (found != null) { if (int) { - data[found!!] = mapOf("id" to i) + data[found!!] = mapOf("id" to index) } val map = (data[found] as Map).toMutableMap() map["defaults"] = list data[found!!] = map } else { - data["inventory_${i}"] = mapOf("id" to i, "defaults" to list) + data["inventory_${index}"] = mapOf("id" to index, "defaults" to list) } - println("$i ${cont.ids!!.mapIndexed { index, it -> "${itemDecoder.getOrNull(it)?.name} ${cont.amounts!![index]}" }.joinToString(separator = ", ")}") +// println("$index ${otherDef.ids!!.mapIndexed { index, it -> "${itemDefinitions.getOrNull(it)?.name} ${otherDef.amounts!![index]}" }.joinToString(separator = ", ")}") } } - cache.update() - println("Shops: $counter") + targetCache.update() + println("Updated $counter inventories.") // yaml.save("inventories.yml", data) } + + @JvmStatic + fun main(args: Array) { + + val target = File("${System.getProperty("user.home")}/Downloads/rs634_cache/") + val other = File("${System.getProperty("user.home")}/Downloads/rs718_cache/") + convert(target, other) + } } \ No newline at end of file diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/convert/SkillDataConverter.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/convert/SkillDataConverter.kt index 5e1f6aa8fa..9e1d80b909 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/convert/SkillDataConverter.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/convert/SkillDataConverter.kt @@ -27,7 +27,7 @@ object SkillDataConverter { val koin = startKoin { modules(module { - single { ItemDefinitions(ItemDecoder().loadCache(get())).load(get(), "./data/definitions/items.yml") } + single { ItemDefinitions(ItemDecoder().load(get())).load(get(), "./data/definitions/items.yml") } single { CacheDelegate("./data/cache/") as Cache } }) }.koin @@ -36,7 +36,7 @@ object SkillDataConverter { val storage: Yaml = get() val items: ItemDefinitions = get() val sounds = SoundDefinitions().load(storage, "./data/definitions/sounds.yml") - val animations = AnimationDefinitions(AnimationDecoder().loadCache(cache)).load(storage, "./data/definitions/animations.yml") + val animations = AnimationDefinitions(AnimationDecoder().load(cache)).load(storage, "./data/definitions/animations.yml") // var decoder = InventoryDecoder(koin.get()) val mapper = ObjectMapper() val yaml = ObjectMapper(YAMLFactory().disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER).apply { diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/definition/item/ItemDefinitionPatcher.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/definition/item/ItemDefinitionPatcher.kt index d59f46970d..6a19105f5e 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/definition/item/ItemDefinitionPatcher.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/definition/item/ItemDefinitionPatcher.kt @@ -11,10 +11,10 @@ object ItemDefinitionPatcher { @JvmStatic fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = ItemDecoder().loadCache(cache) + val decoder = ItemDecoder().load(cache) val yaml = Yaml() - val current = ItemDefinitions(ItemDecoder().loadCache(cache)).load(yaml, property("itemDefinitionsPath")) - val newer = ItemDefinitions(ItemDecoder().loadCache(cache)).load(yaml, "./items.yml") + val current = ItemDefinitions(ItemDecoder().load(cache)).load(yaml, property("itemDefinitionsPath")) + val newer = ItemDefinitions(ItemDecoder().load(cache)).load(yaml, "./items.yml") val map = mutableMapOf() for (id in decoder.indices) { val def = current.getOrNull(id) ?: continue diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/definition/item/ItemDefinitionPipeline.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/definition/item/ItemDefinitionPipeline.kt index 89da0e339e..35bb54d48e 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/definition/item/ItemDefinitionPipeline.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/definition/item/ItemDefinitionPipeline.kt @@ -55,7 +55,7 @@ object ItemDefinitionPipeline { val start = System.currentTimeMillis() val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = ItemDecoder().loadCache(cache) + val decoder = ItemDecoder().load(cache) val pages = getPages(decoder, rs2Wiki) val output = buildItemExtras(revisionDate, decoder, cache718, rs2Wiki, pages) diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/definition/item/pipe/extra/ItemEquipmentInfo.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/definition/item/pipe/extra/ItemEquipmentInfo.kt index 9b2d6339e7..878be274fe 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/definition/item/pipe/extra/ItemEquipmentInfo.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/definition/item/pipe/extra/ItemEquipmentInfo.kt @@ -12,7 +12,7 @@ class ItemEquipmentInfo(decoder: Array, val cache: Cache) : Pipe init { // Load equip slots and types - ItemDecoder718().loadCache(cache) + ItemDecoder718().load(cache) } private val types = ItemTypes(decoder) diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/definition/npc/NPCDefinitionPipeline.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/definition/npc/NPCDefinitionPipeline.kt index bb0775b09c..d49e7f3065 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/definition/npc/NPCDefinitionPipeline.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/definition/npc/NPCDefinitionPipeline.kt @@ -32,7 +32,7 @@ object NPCDefinitionPipeline { val rs2Wiki = Wiki.load("${System.getProperty("user.home")}\\Downloads\\runescape_pages_full\\runescapewiki-latest-pages-articles-2011-01-31.xml") val start = System.currentTimeMillis() val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = NPCDecoder(true).loadCache(cache) + val decoder = NPCDecoder(true).load(cache) val pages = getPages(decoder, rs2Wiki) val output = buildNPCExtras(decoder, pages) val map = convertToYaml(output) diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/definition/obj/ObjectDefinitionPipeline.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/definition/obj/ObjectDefinitionPipeline.kt index dca42a9326..c58639b874 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/definition/obj/ObjectDefinitionPipeline.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/definition/obj/ObjectDefinitionPipeline.kt @@ -51,7 +51,7 @@ private object ObjectDefinitionPipeline { fun main(args: Array) { val start = System.currentTimeMillis() val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = ObjectDecoderFull(members = true, lowDetail = false).loadCache(cache) + val decoder = ObjectDecoderFull(members = true, lowDetail = false).load(cache) val pages = decoder.indices.mapNotNull { val def = decoder.getOrNull(it) if (def != null) { diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/definition/obj/pipe/ObjectDoorsGates.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/definition/obj/pipe/ObjectDoorsGates.kt index 09be976cca..4cf6ad055a 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/definition/obj/pipe/ObjectDoorsGates.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/definition/obj/pipe/ObjectDoorsGates.kt @@ -108,7 +108,7 @@ class ObjectDoorsGates(private val decoder: Array) : Pipel @JvmStatic fun main(args: Array) { val cache = CacheDelegate("./data/cache") - val decoder = ObjectDecoderFull(false, true).loadCache(cache) + val decoder = ObjectDecoderFull(false, true).load(cache) val gates = ObjectDoorsGates(decoder) val match = gates.match(decoder[45849]) println("Match $match") diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/detail/AnimationNames.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/detail/AnimationNames.kt index ec7c3a73c0..ba6144b0ae 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/detail/AnimationNames.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/detail/AnimationNames.kt @@ -22,8 +22,8 @@ object AnimationNames { fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) val yaml = Yaml() - val decoder = AnimationDecoderFull().loadCache(cache) - val itemDecoder = ItemDecoderFull().loadCache(cache) + val decoder = AnimationDecoderFull().load(cache) + val itemDecoder = ItemDecoderFull().load(cache) val renders = getRenderAnimations(cache) val map = mutableMapOf>() for (id in decoder.indices) { @@ -50,7 +50,7 @@ object AnimationNames { private fun getRenderAnimations(cache: Cache): Map { val renders = getNPCRenderIds(cache) - val decoder = RenderAnimationDecoder().loadCache(cache) + val decoder = RenderAnimationDecoder().load(cache) val map = mutableMapOf() for (id in decoder.indices) { val def = decoder.getOrNull(id) ?: continue @@ -75,7 +75,7 @@ object AnimationNames { private fun getNPCRenderIds(cache: Cache): Map { val map = mutableMapOf() - val decoder = NPCDecoder(member = true).loadCache(cache) + val decoder = NPCDecoder(member = true).load(cache) for (id in decoder.indices) { val def = decoder.getOrNull(id) ?: continue map[def.renderEmote] = def.name diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/detail/GraphicNames.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/detail/GraphicNames.kt index b624ae1fe6..92550e778a 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/detail/GraphicNames.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/detail/GraphicNames.kt @@ -26,7 +26,7 @@ object GraphicNames { addNPCModels(cache, models) addObjectModels(cache, models) val yaml = Yaml() - val decoder = GraphicDecoder().loadCache(cache) + val decoder = GraphicDecoder().load(cache) val map = mutableMapOf>() for (id in decoder.indices) { val def = decoder.getOrNull(id) ?: continue @@ -43,7 +43,7 @@ object GraphicNames { } private fun addItemModels(cache: Cache, models: MutableMap>) { - val decoder = ItemDecoderFull().loadCache(cache) + val decoder = ItemDecoderFull().load(cache) for (id in decoder.indices) { val def = decoder.getOrNull(id) ?: continue models.add(def.primaryMaleModel, def.name) @@ -57,7 +57,7 @@ object GraphicNames { } private fun addNPCModels(cache: Cache, models: MutableMap>) { - val decoder = NPCDecoderFull(members = true).loadCache(cache) + val decoder = NPCDecoderFull(members = true).load(cache) for (id in decoder.indices) { val def = decoder.getOrNull(id) ?: continue def.modelIds?.forEach { model -> @@ -67,7 +67,7 @@ object GraphicNames { } private fun addObjectModels(cache: Cache, models: MutableMap>) { - val decoder = ObjectDecoderFull(members = true, lowDetail = false).loadCache(cache) + val decoder = ObjectDecoderFull(members = true, lowDetail = false).load(cache) for (id in decoder.indices) { val def = decoder.getOrNull(id) ?: continue def.modelIds?.forEach { array -> diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/detail/NPCNames.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/detail/NPCNames.kt index d5854d0928..b257419a59 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/detail/NPCNames.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/detail/NPCNames.kt @@ -16,7 +16,7 @@ private class NPCNames(val decoder: Array) : NameDumper() { @JvmStatic fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = NPCDecoder(member = true).loadCache(cache) + val decoder = NPCDecoder(member = true).load(cache) val yaml= Yaml() val names = NPCNames(decoder) names.dump(yaml, "./npc-details.yml", "npc", decoder.lastIndex) diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/graph/MapGraph.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/graph/MapGraph.kt index 3000bfdca0..68fa555853 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/graph/MapGraph.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/graph/MapGraph.kt @@ -5,7 +5,7 @@ import world.gregs.voidps.cache.Cache import world.gregs.voidps.engine.entity.obj.GameObject import world.gregs.voidps.engine.entity.obj.GameObjects import world.gregs.voidps.engine.map.collision.Collisions -import world.gregs.voidps.engine.map.region.Xteas +import world.gregs.voidps.tools.cache.Xteas import world.gregs.voidps.type.Direction import world.gregs.voidps.type.Distance import world.gregs.voidps.type.Region @@ -37,7 +37,7 @@ class MapGraph( for (region in reg.toCuboid(width = 33, height = 23).toRegions()) { // TODO better way of determining empty maps val xtea = xteas[region.id] - cache.getFile(5, "l${region.x}_${region.y}", xtea) ?: continue + cache.data(5, "l${region.x}_${region.y}", xtea) ?: continue for (zone in region.tile.zone.toCuboid(width = 8, height = 8).toZones()) { val time = measureNanoTime { diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/graph/MapGraphLoader.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/graph/MapGraphLoader.kt index 506a844b45..d788922971 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/graph/MapGraphLoader.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/graph/MapGraphLoader.kt @@ -8,7 +8,7 @@ import world.gregs.voidps.engine.data.definition.ObjectDefinitions import world.gregs.voidps.engine.entity.obj.GameObjects import world.gregs.voidps.engine.map.collision.Collisions import world.gregs.voidps.engine.map.collision.GameObjectCollision -import world.gregs.voidps.engine.map.region.Xteas +import world.gregs.voidps.tools.cache.Xteas import world.gregs.voidps.tools.property import world.gregs.yaml.Yaml @@ -18,7 +18,7 @@ object MapGraphLoader { fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) val collisions: Collisions = Collisions() - val objectDefinitions = ObjectDefinitions(ObjectDecoder(member = true, lowDetail = false).loadCache(cache)) + val objectDefinitions = ObjectDefinitions(ObjectDecoder(member = true, lowDetail = false).load(cache)) .load(Yaml(), property("objectDefinitionsPath")) val objects = GameObjects(GameObjectCollision(Collisions()), ZoneBatchUpdates(), objectDefinitions) val xteas: Xteas = Xteas().load() diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/map/MapPacker.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/map/MapPacker.kt index 941d894129..e16c41893c 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/map/MapPacker.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/map/MapPacker.kt @@ -5,7 +5,7 @@ import kotlinx.coroutines.runBlocking import org.jsoup.Jsoup import world.gregs.voidps.cache.CacheDelegate import world.gregs.voidps.cache.Index -import world.gregs.voidps.engine.map.region.Xteas +import world.gregs.voidps.tools.cache.Xteas import world.gregs.voidps.type.Region import java.io.File @@ -14,28 +14,37 @@ import java.io.File */ object MapPacker { + fun pack634(target: File, targetXteas: Xteas, cache727: File, xteas727: Xteas, cache681: File, xteas681: Xteas, cache537: File) { + val cache = CacheDelegate(target.path) + packMissingMaps(cache, targetXteas, CacheDelegate(cache727.path), xteas727, all()) + packMissingMaps(cache, targetXteas, CacheDelegate(cache681.path), xteas681, all()) // revision 681 + packEaster08Map(cache, CacheDelegate(cache537.path)) // revision 537 + } + @JvmStatic fun main(args: Array) { - val target = CacheDelegate("${System.getProperty("user.home")}/Downloads/rs634_cache/") - val xteas = Xteas().load("./data/xteas.dat", Xteas.DEFAULT_KEY, Xteas.DEFAULT_VALUE) - packMissingMaps(target, xteas, CacheDelegate("${System.getProperty("user.home")}/Downloads/727 cache with most xteas/"), Xteas(), all()) - packMissingMaps(target, xteas, CacheDelegate("${System.getProperty("user.home")}/Downloads/cache-280/"), getKeys(280), all()) // revision 681 - packEaster08Map(target, CacheDelegate("${System.getProperty("user.home")}/Downloads/cache-257/")) // revision 537 + val target = File("${System.getProperty("user.home")}/Downloads/rs634_cache/") + val xteas = Xteas().load("./tools/src/main/resources/xteas.dat", Xteas.DEFAULT_KEY, Xteas.DEFAULT_VALUE) + val cache727 = File("${System.getProperty("user.home")}/Downloads/727 cache with most xteas/") + val cache681 = File("${System.getProperty("user.home")}/Downloads/cache-280/") + val xteas681 = getKeys(280) + val cache537 = File("${System.getProperty("user.home")}/Downloads/cache-257/") + pack634(target, xteas, cache727, Xteas(), cache681, xteas681, cache537) } private fun packMissingMaps(target: CacheDelegate, sourceXteas: Xteas, source: CacheDelegate, targetXteas: Xteas, regions: List) { val invalid = mutableSetOf() runBlocking { - val archives = target.getArchives(Index.MAPS).toSet() + val archives = target.archives(Index.MAPS).toSet() for (region in regions) { - val archive = target.getArchiveId(Index.MAPS, "l${region.x}_${region.y}") + val archive = target.archiveId(Index.MAPS, "l${region.x}_${region.y}") if (!archives.contains(archive)) { continue } - val data = target.getFile(Index.MAPS, archive, 0, sourceXteas[region]) + val data = target.data(Index.MAPS, archive, 0, sourceXteas[region]) if (data == null) { - val objData = source.getFile(Index.MAPS, "l${region.x}_${region.y}", targetXteas[region]) - val tileData = source.getFile(Index.MAPS, "m${region.x}_${region.y}") + val objData = source.data(Index.MAPS, "l${region.x}_${region.y}", targetXteas[region]) + val tileData = source.data(Index.MAPS, "m${region.x}_${region.y}") if (objData == null || tileData == null) { println("Can't find map $region") } else { @@ -53,8 +62,8 @@ object MapPacker { private fun packEaster08Map(target: CacheDelegate, source: CacheDelegate) { val region = Region(9811) - val objData = source.getFile(Index.MAPS, "l${region.x}_${region.y}", intArrayOf(-929935426, 1005492936, -2143736251, 386758357)) - val tileData = source.getFile(Index.MAPS, "m${region.x}_${region.y}") + val objData = source.data(Index.MAPS, "l${region.x}_${region.y}", intArrayOf(-929935426, 1005492936, -2143736251, 386758357)) + val tileData = source.data(Index.MAPS, "m${region.x}_${region.y}") if (objData == null || tileData == null) { println("Can't find map $region") } else { diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/map/MissingMapFinder.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/map/MissingMapFinder.kt index 54271b402d..1d34624ad8 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/map/MissingMapFinder.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/map/MissingMapFinder.kt @@ -8,7 +8,7 @@ import kotlinx.coroutines.runBlocking import org.jsoup.Jsoup import world.gregs.voidps.cache.CacheDelegate import world.gregs.voidps.cache.Index -import world.gregs.voidps.engine.map.region.Xteas +import world.gregs.voidps.tools.cache.Xteas import world.gregs.voidps.type.Region import java.io.File @@ -125,15 +125,15 @@ object MissingMapFinder { } val invalid = mutableSetOf() runBlocking { - val archives = cache.getArchives(Index.MAPS).toSet() + val archives = cache.archives(Index.MAPS).toSet() for (regionX in 0 until 256) { for (regionY in 0 until 256) { val region = Region(regionX, regionY) - val archive = cache.getArchiveId(Index.MAPS, "l${regionX}_${regionY}") + val archive = cache.archiveId(Index.MAPS, "l${regionX}_${regionY}") if (!archives.contains(archive)) { continue } - val data = cache.getFile(Index.MAPS, archive, 0, xteas[region]) + val data = cache.data(Index.MAPS, archive, 0, xteas[region]) if (data == null) { invalid.add(region) } diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/map/obj/ObjectUsageFinder.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/map/obj/ObjectUsageFinder.kt index 9513406473..2aa7c2f8d9 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/map/obj/ObjectUsageFinder.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/map/obj/ObjectUsageFinder.kt @@ -6,7 +6,7 @@ import world.gregs.voidps.cache.definition.data.MapObject import world.gregs.voidps.cache.definition.data.ObjectDefinitionFull import world.gregs.voidps.cache.definition.decoder.MapDecoder import world.gregs.voidps.cache.definition.decoder.ObjectDecoderFull -import world.gregs.voidps.engine.map.region.Xteas +import world.gregs.voidps.tools.cache.Xteas import world.gregs.voidps.tools.property import world.gregs.voidps.tools.propertyOrNull import world.gregs.voidps.type.Region @@ -17,8 +17,8 @@ object ObjectUsageFinder { fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) val xteas: Xteas = Xteas().load(property("xteaPath"), propertyOrNull("xteaJsonKey") ?: Xteas.DEFAULT_KEY, propertyOrNull("xteaJsonValue") ?: Xteas.DEFAULT_VALUE) - val decoder = ObjectDecoderFull(members = false, lowDetail = false).loadCache(cache) - val maps = MapDecoder(xteas).loadCache(cache) + val decoder = ObjectDecoderFull(members = false, lowDetail = false).load(cache) + val maps = MapDecoder(xteas).load(cache) for (map in maps) { val region = Region(map.id) for (obj in map.objects) { diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/map/obj/WorldMapLinkIdentifier.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/map/obj/WorldMapLinkIdentifier.kt index edba9c4b53..e376cf7b68 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/map/obj/WorldMapLinkIdentifier.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/map/obj/WorldMapLinkIdentifier.kt @@ -9,10 +9,10 @@ import world.gregs.voidps.engine.client.update.batch.ZoneBatchUpdates import world.gregs.voidps.engine.data.definition.ObjectDefinitions import world.gregs.voidps.engine.entity.obj.GameObject import world.gregs.voidps.engine.entity.obj.GameObjects -import world.gregs.voidps.engine.map.collision.CollisionReader +import world.gregs.voidps.engine.map.collision.CollisionDecoder import world.gregs.voidps.engine.map.collision.Collisions import world.gregs.voidps.engine.map.collision.GameObjectCollision -import world.gregs.voidps.engine.map.region.Xteas +import world.gregs.voidps.tools.cache.Xteas import world.gregs.voidps.tools.map.view.graph.MutableNavigationGraph import world.gregs.voidps.tools.property import world.gregs.voidps.tools.propertyOrNull @@ -29,20 +29,20 @@ object WorldMapLinkIdentifier { fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) val xteas: Xteas = Xteas().load(property("xteaPath"), propertyOrNull("xteaJsonKey") ?: Xteas.DEFAULT_KEY, propertyOrNull("xteaJsonValue") ?: Xteas.DEFAULT_VALUE) - val worldMapDetailsDecoder = WorldMapDetailsDecoder().loadCache(cache) - val worldMapIconDecoder = WorldMapIconDecoder().loadCache(cache) - val definitions: ObjectDefinitions = ObjectDefinitions(ObjectDecoder(member = true, lowDetail = false).loadCache(cache)).load(Yaml(), property("objectDefinitionsPath")) - val mapDecoder = MapDecoder(xteas).loadCache(cache) + val worldMapDetailsDecoder = WorldMapDetailsDecoder().load(cache) + val worldMapIconDecoder = WorldMapIconDecoder().load(cache) + val definitions: ObjectDefinitions = ObjectDefinitions(ObjectDecoder(member = true, lowDetail = false).load(cache)).load(Yaml(), property("objectDefinitionsPath")) + val mapDecoder = MapDecoder(xteas).load(cache) val collisions = Collisions() - val collisionReader = CollisionReader(collisions) + val collisionDecoder = CollisionDecoder(collisions) val graph = MutableNavigationGraph() val linker = ObjectLinker(collisions) - val clientScriptDecoder = ClientScriptDecoder(revision634 = true).loadCache(cache) + val clientScriptDecoder = ClientScriptDecoder().load(cache) val objects = GameObjects(GameObjectCollision(collisions), ZoneBatchUpdates(), definitions) val regions = mutableListOf() for (regionX in 0 until 256) { for (regionY in 0 until 256) { - cache.getFile(5, "m${regionX}_${regionY}") ?: continue + cache.data(5, "m${regionX}_${regionY}") ?: continue regions.add(Region(regionX, regionY)) } } @@ -63,7 +63,7 @@ object WorldMapLinkIdentifier { objects.add(obj) objCollision.modify(obj, add = true) } - collisionReader.read(region, def) + collisionDecoder.decode(region, def) } val cacheLinks = mutableListOf>() val dungeons = WorldMapDungeons(worldMapDetailsDecoder, worldMapIconDecoder, clientScriptDecoder, cache) diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/map/render/WorldMapDumper.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/map/render/WorldMapDumper.kt index 99d1c7be28..7e6ec87b25 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/map/render/WorldMapDumper.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/map/render/WorldMapDumper.kt @@ -4,23 +4,24 @@ import org.koin.core.context.startKoin import org.koin.dsl.module import org.koin.fileProperties import world.gregs.voidps.cache.Cache -import world.gregs.voidps.cache.CacheDelegate +import world.gregs.voidps.cache.MemoryCache import world.gregs.voidps.cache.config.decoder.MapSceneDecoder import world.gregs.voidps.cache.config.decoder.OverlayDecoder import world.gregs.voidps.cache.config.decoder.UnderlayDecoder import world.gregs.voidps.cache.config.decoder.WorldMapInfoDecoder import world.gregs.voidps.cache.definition.decoder.* -import world.gregs.voidps.engine.map.region.Xteas import world.gregs.voidps.tools.Pipeline +import world.gregs.voidps.tools.cache.Xteas import world.gregs.voidps.tools.map.render.draw.MinimapIconPainter import world.gregs.voidps.tools.map.render.draw.RegionRenderer import world.gregs.voidps.tools.map.render.load.MapTileSettings import world.gregs.voidps.tools.map.render.load.RegionManager import world.gregs.voidps.type.Region import java.io.File +import java.util.concurrent.TimeUnit /** - * Renders and saves all regions to individual pngs + * Renders and saves all regions to individual pngs with various levels of detail */ object WorldMapDumper { @@ -32,24 +33,24 @@ object WorldMapDumper { fileProperties("/tool.properties") modules( module { - single { CacheDelegate(getProperty("cachePath")) as Cache } + single { MemoryCache(getProperty("cachePath")) as Cache } single { MapDecoder(get()) } single(createdAtStart = true) { - Xteas().load(getProperty("xteaPath"), getPropertyOrNull("xteaJsonKey") ?: Xteas.DEFAULT_KEY, getPropertyOrNull("xteaJsonValue") ?: Xteas.DEFAULT_VALUE) + Xteas()//.load(getProperty("xteaPath"), getPropertyOrNull("xteaJsonKey") ?: Xteas.DEFAULT_KEY, getPropertyOrNull("xteaJsonValue") ?: Xteas.DEFAULT_VALUE) } }) }.koin val cache: Cache = koin.get() - val mapDefinitions = MapDecoder(koin.get()).loadCache(cache).associateBy { it.id } - val objectDecoder = ObjectDecoderFull(members = true, lowDetail = false).loadCache(cache) - val overlayDefinitions = OverlayDecoder().loadCache(cache) - val underlayDefinitions = UnderlayDecoder().loadCache(cache) - val textureDefinitions = TextureDecoder().loadCache(cache) - val worldMapDecoder = WorldMapDetailsDecoder().loadCache(cache) - val worldMapInfoDecoder = WorldMapInfoDecoder().loadCache(cache) - val spriteDecoder = SpriteDecoder().loadCache(cache) - val mapSceneDecoder = MapSceneDecoder().loadCache(cache) + val mapDefinitions = MapDecoder(koin.get()).load(cache).associateBy { it.id } + val objectDecoder = ObjectDecoderFull(members = true, lowDetail = false).load(cache) + val overlayDefinitions = OverlayDecoder().load(cache) + val underlayDefinitions = UnderlayDecoder().load(cache) + val textureDefinitions = TextureDecoder().load(cache) + val worldMapDecoder = WorldMapDetailsDecoder().load(cache) + val worldMapInfoDecoder = WorldMapInfoDecoder().load(cache) + val spriteDecoder = SpriteDecoder().load(cache) + val mapSceneDecoder = MapSceneDecoder().load(cache) File("./images/").mkdir() for (i in 0 until 4) { @@ -67,7 +68,7 @@ object WorldMapDumper { val regions = mutableListOf() for (regionX in 0 until 256) { for (regionY in 0 until 256) { - cache.getFile(5, "m${regionX}_${regionY}") ?: continue + cache.data(5, "m${regionX}_${regionY}") ?: continue regions.add(Region(regionX, regionY)) } } @@ -76,6 +77,6 @@ object WorldMapDumper { regions.forEach { pipeline.process(it) } - println("${regions.size} regions loaded in ${System.currentTimeMillis() - start}ms") + println("${regions.size} regions loaded in ${TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - start)}s") } } \ No newline at end of file diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/map/render/draw/MinimapIconPainter.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/map/render/draw/MinimapIconPainter.kt index b7a473c409..9b94f81a0e 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/map/render/draw/MinimapIconPainter.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/map/render/draw/MinimapIconPainter.kt @@ -25,8 +25,8 @@ class MinimapIconPainter( fun startup(cache: Cache) { val stringId = "${worldMapDefinitions[DEFAULTS].map}_staticelements" - val archiveId = cache.getArchiveId(WORLD_MAP, stringId) - var length = cache.archiveCount(WORLD_MAP, archiveId) + val archiveId = cache.archiveId(WORLD_MAP, stringId) + var length = cache.fileCount(WORLD_MAP, archiveId) val positions = IntArray(length) val ids = IntArray(length) @@ -34,7 +34,7 @@ class MinimapIconPainter( var counter = 0 var index = 0 while (length > counter) { - val file = cache.getFile(WORLD_MAP, archiveId, index++) ?: continue + val file = cache.data(WORLD_MAP, archiveId, index++) ?: continue val buffer = BufferReader(file) val position = buffer.readInt() val id = buffer.readShort() diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/map/view/MapViewer.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/map/view/MapViewer.kt index d018b93852..7f939491fc 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/map/view/MapViewer.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/map/view/MapViewer.kt @@ -21,7 +21,7 @@ class MapViewer { frame.defaultCloseOperation = JFrame.EXIT_ON_CLOSE val cache = CacheDelegate("./data/cache/") val yaml = Yaml() - val decoder = ObjectDecoder(member = false, lowDetail = false).loadCache(cache) + val decoder = ObjectDecoder(member = false, lowDetail = false).load(cache) val defs = ObjectDefinitions(decoder).load(yaml, "./data/definitions/objects.yml") val areas = AreaDefinitions().load(yaml, "./data/map/areas.yml") val nav = NavigationGraph(defs, areas).load(yaml, "./data/map/nav-graph.yml") diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/map/xtea/CacheMapDecryption.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/map/xtea/CacheMapDecryption.kt index a8ec479050..26308f25ef 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/map/xtea/CacheMapDecryption.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/map/xtea/CacheMapDecryption.kt @@ -3,7 +3,7 @@ package world.gregs.voidps.tools.map.xtea import kotlinx.coroutines.runBlocking import world.gregs.voidps.cache.CacheDelegate import world.gregs.voidps.cache.Index -import world.gregs.voidps.engine.map.region.Xteas +import world.gregs.voidps.tools.cache.Xteas import world.gregs.voidps.type.Region object CacheMapDecryption { @@ -13,17 +13,17 @@ object CacheMapDecryption { val cache = CacheDelegate("./data/cache_decrypted/") val xteas = Xteas().load("./xteas/") var count = 0 - val archives = cache.getArchives(Index.MAPS).toSet() + val archives = cache.archives(Index.MAPS).toSet() for (regionX in 0 until 256) { for (regionY in 0 until 256) { val region = Region(regionX, regionY) - val archive = cache.getArchiveId(Index.MAPS, "l${regionX}_${regionY}") + val archive = cache.archiveId(Index.MAPS, "l${regionX}_${regionY}") if (!archives.contains(archive)) { continue } val keys = xteas[region] if (keys != null) { - val data = cache.getFile(Index.MAPS, archive, 0, keys)!! + val data = cache.data(Index.MAPS, archive, 0, keys)!! cache.write(Index.MAPS, archive, 0, data) count++ } diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/map/xtea/XteaCrossReferencer.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/map/xtea/XteaCrossReferencer.kt index 5c3660bc75..9d3aba42f6 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/map/xtea/XteaCrossReferencer.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/map/xtea/XteaCrossReferencer.kt @@ -2,7 +2,7 @@ package world.gregs.voidps.tools.map.xtea import com.displee.cache.CacheLibrary import world.gregs.voidps.cache.Index -import world.gregs.voidps.engine.map.region.Xteas +import world.gregs.voidps.tools.cache.Xteas import world.gregs.voidps.type.Region import java.io.File import java.io.RandomAccessFile @@ -43,7 +43,7 @@ object XteaCrossReferencer { @JvmStatic fun main(args: Array) { val library = CacheLibrary("./data/cache/") - val xteas = Xteas().load(path = "./data/xteas.dat") + val xteas = Xteas().load(path = "./tools/src/main/resources/xteas.dat") val xteasList = all("${System.getProperty("user.home")}\\Downloads\\rs634_cache\\xteas\\xteas.dat") more(xteasList) diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/map/xtea/XteaPacker.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/map/xtea/XteaPacker.kt index 0c3fb2305f..6fca314664 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/map/xtea/XteaPacker.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/map/xtea/XteaPacker.kt @@ -1,7 +1,7 @@ package world.gregs.voidps.tools.map.xtea import kotlinx.coroutines.runBlocking -import world.gregs.voidps.engine.map.region.Xteas +import world.gregs.voidps.tools.cache.Xteas import java.io.RandomAccessFile object XteaPacker { diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/map/xtea/XteaValidator.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/map/xtea/XteaValidator.kt index 762549c755..1ac8fe1fa9 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/map/xtea/XteaValidator.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/map/xtea/XteaValidator.kt @@ -3,28 +3,28 @@ package world.gregs.voidps.tools.map.xtea import kotlinx.coroutines.runBlocking import world.gregs.voidps.cache.CacheDelegate import world.gregs.voidps.cache.Index -import world.gregs.voidps.engine.map.region.Xteas +import world.gregs.voidps.tools.cache.Xteas import world.gregs.voidps.type.Region object XteaValidator { @JvmStatic fun main(args: Array) = runBlocking { - val cache = CacheDelegate("${System.getProperty("user.home")}/Downloads/rs634_cache/") - val xteas = Xteas().load("./data/xteas.dat", Xteas.DEFAULT_KEY, Xteas.DEFAULT_VALUE) + val cache = CacheDelegate("./data/cache/new/") + val xteas = Xteas().load("./tools/src/main/resources/xteas.dat", Xteas.DEFAULT_KEY, Xteas.DEFAULT_VALUE) - val archives = cache.getArchives(Index.MAPS).toSet() + val archives = cache.archives(Index.MAPS).toSet() var total = 0 var valid = 0 val invalid = mutableSetOf() for (regionX in 0 until 256) { for (regionY in 0 until 256) { val region = Region(regionX, regionY) - val archive = cache.getArchiveId(Index.MAPS, "l${regionX}_${regionY}") + val archive = cache.archiveId(Index.MAPS, "l${regionX}_${regionY}") if (!archives.contains(archive)) { continue } total++ - val data = cache.getFile(Index.MAPS, archive, 0, xteas[region]) + val data = cache.data(Index.MAPS, archive, 0, xteas[region]) if (data == null) { if (xteas.containsKey(region.id)) { println("Failed key ${region.id} $archive ${xteas[region]?.toList()}") diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/wiki/MusicInfoBoxDumper.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/wiki/MusicInfoBoxDumper.kt index 378309dc0e..bee28651c9 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/wiki/MusicInfoBoxDumper.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/wiki/MusicInfoBoxDumper.kt @@ -34,7 +34,7 @@ object MusicInfoBoxDumper { val output = mutableMapOf() val cache = CacheDelegate("./data/cache/") - val defs = EnumDecoder().loadCache(cache) + val defs = EnumDecoder().load(cache) val enum = defs[1345] val enumMap = enum.map!!.mapValues { (_, value) -> toIdentifier(value as String) } println(enumMap) diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/wiki/scrape/RunescapeWikiModifier.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/wiki/scrape/RunescapeWikiModifier.kt index 6ccfb3b8da..b72091b376 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/wiki/scrape/RunescapeWikiModifier.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/wiki/scrape/RunescapeWikiModifier.kt @@ -22,7 +22,7 @@ internal object RunescapeWikiModifier { @JvmStatic fun main(args: Array) { val cache: Cache = CacheDelegate(property("cachePath")) - val decoder = ItemDecoder().loadCache(cache) + val decoder = ItemDecoder().load(cache) val file = File("./data/dump/ItemsPretty.json") val text = file.readText() diff --git a/tools/src/main/resources/keywords.txt b/tools/src/main/resources/keywords.txt index b99e47d053..cb030fe5ae 100644 --- a/tools/src/main/resources/keywords.txt +++ b/tools/src/main/resources/keywords.txt @@ -7703,4 +7703,7 @@ zygomite zygomites zz hsl -hsv \ No newline at end of file +hsv +windows/x86/ +windows/x86_64/ +linux/x86/ \ No newline at end of file diff --git a/tools/src/main/resources/tool.properties b/tools/src/main/resources/tool.properties index dc63fde3e7..2e5f1dbac6 100644 --- a/tools/src/main/resources/tool.properties +++ b/tools/src/main/resources/tool.properties @@ -2,7 +2,7 @@ name=Void cachePath=./data/cache/ savePath=./data/saves/ -xteaPath=./data/xteas.dat +xteaPath=./tools/src/main/resources/xteas.dat scriptModule=engine homeX=3087 homeY=3497 diff --git a/engine/src/test/kotlin/world/gregs/voidps/engine/map/region/XteasTest.kt b/tools/src/test/kotlin/world/gregs/voidps/tools/cache/XteasTest.kt similarity index 90% rename from engine/src/test/kotlin/world/gregs/voidps/engine/map/region/XteasTest.kt rename to tools/src/test/kotlin/world/gregs/voidps/tools/cache/XteasTest.kt index b7e5439804..3c69ba3cd4 100644 --- a/engine/src/test/kotlin/world/gregs/voidps/engine/map/region/XteasTest.kt +++ b/tools/src/test/kotlin/world/gregs/voidps/tools/cache/XteasTest.kt @@ -1,12 +1,12 @@ -package world.gregs.voidps.engine.map.region +package world.gregs.voidps.tools.cache import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows -import world.gregs.voidps.engine.map.region.Xteas.Companion.DEFAULT_KEY -import world.gregs.voidps.engine.map.region.Xteas.Companion.DEFAULT_VALUE -import world.gregs.voidps.engine.map.region.Xteas.Companion.loadJson +import world.gregs.voidps.tools.cache.Xteas.Companion.DEFAULT_KEY +import world.gregs.voidps.tools.cache.Xteas.Companion.DEFAULT_VALUE +import world.gregs.voidps.tools.cache.Xteas.Companion.loadJson internal class XteasTest { diff --git a/engine/src/test/resources/xteas/123.dat b/tools/src/test/resources/xteas/123.dat similarity index 100% rename from engine/src/test/resources/xteas/123.dat rename to tools/src/test/resources/xteas/123.dat diff --git a/engine/src/test/resources/xteas/123.txt b/tools/src/test/resources/xteas/123.txt similarity index 100% rename from engine/src/test/resources/xteas/123.txt rename to tools/src/test/resources/xteas/123.txt diff --git a/engine/src/test/resources/xteas/xteas-default.json b/tools/src/test/resources/xteas/xteas-default.json similarity index 100% rename from engine/src/test/resources/xteas/xteas-default.json rename to tools/src/test/resources/xteas/xteas-default.json diff --git a/engine/src/test/resources/xteas/xteas.json b/tools/src/test/resources/xteas/xteas.json similarity index 100% rename from engine/src/test/resources/xteas/xteas.json rename to tools/src/test/resources/xteas/xteas.json