forked from openrs2/openrs2
This is relatively easy as OpenNXT doesn't use the actual SQLite cache format - it still uses JS5-compressed containers, rather than ZLIB. Signed-off-by: Graham <gpe@openrs2.org>master
parent
dbb30e0bd8
commit
39d2f18cca
@ -0,0 +1,26 @@ |
|||||||
|
package org.openrs2.cache.cli |
||||||
|
|
||||||
|
import com.github.ajalt.clikt.core.CliktCommand |
||||||
|
import com.github.ajalt.clikt.parameters.arguments.argument |
||||||
|
import com.github.ajalt.clikt.parameters.types.path |
||||||
|
import com.google.inject.Guice |
||||||
|
import io.netty.buffer.ByteBufAllocator |
||||||
|
import org.openrs2.cache.CacheModule |
||||||
|
import org.openrs2.cache.OpenNxtStore |
||||||
|
import org.openrs2.cache.Store |
||||||
|
import org.openrs2.inject.CloseableInjector |
||||||
|
|
||||||
|
public class OpenNxtUnpackCommand : CliktCommand(name = "unpack-opennxt") { |
||||||
|
private val input by argument().path(mustExist = true, canBeFile = false, mustBeReadable = true) |
||||||
|
private val output by argument().path(canBeFile = false, mustBeReadable = true, mustBeWritable = true) |
||||||
|
|
||||||
|
override fun run() { |
||||||
|
CloseableInjector(Guice.createInjector(CacheModule)).use { injector -> |
||||||
|
val alloc = injector.getInstance(ByteBufAllocator::class.java) |
||||||
|
|
||||||
|
Store.open(output, alloc).use { store -> |
||||||
|
OpenNxtStore.unpack(input, store) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,85 @@ |
|||||||
|
package org.openrs2.cache |
||||||
|
|
||||||
|
import io.netty.buffer.Unpooled |
||||||
|
import org.openrs2.buffer.crc32 |
||||||
|
import org.openrs2.buffer.use |
||||||
|
import org.sqlite.SQLiteDataSource |
||||||
|
import java.nio.file.Files |
||||||
|
import java.nio.file.Path |
||||||
|
import java.sql.Connection |
||||||
|
|
||||||
|
public object OpenNxtStore { |
||||||
|
public fun unpack(input: Path, output: Store) { |
||||||
|
output.create(Store.ARCHIVESET) |
||||||
|
|
||||||
|
for (archive in 0..Store.MAX_ARCHIVE) { |
||||||
|
val path = input.resolve("js5-$archive.jcache") |
||||||
|
if (!Files.exists(path)) { |
||||||
|
continue |
||||||
|
} |
||||||
|
|
||||||
|
val dataSource = SQLiteDataSource() |
||||||
|
dataSource.url = "jdbc:sqlite:$path" |
||||||
|
|
||||||
|
dataSource.connection.use { connection -> |
||||||
|
unpackArchive(connection, archive, output) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun unpackArchive(connection: Connection, archive: Int, output: Store) { |
||||||
|
connection.prepareStatement(""" |
||||||
|
SELECT data, crc |
||||||
|
FROM cache_index |
||||||
|
WHERE key = 1 |
||||||
|
""".trimIndent()).use { stmt -> |
||||||
|
stmt.executeQuery().use { rows -> |
||||||
|
if (rows.next()) { |
||||||
|
val checksum = rows.getInt(2) |
||||||
|
|
||||||
|
Unpooled.wrappedBuffer(rows.getBytes(1)).use { buf -> |
||||||
|
val actualChecksum = buf.crc32() |
||||||
|
if (actualChecksum != checksum) { |
||||||
|
throw StoreCorruptException( |
||||||
|
"Js5Index corrupt (expected checksum $checksum, actual checksum $actualChecksum)" |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
output.write(Store.ARCHIVESET, archive, buf) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
connection.prepareStatement(""" |
||||||
|
SELECT key, data, crc, version |
||||||
|
FROM cache |
||||||
|
""".trimIndent()).use { stmt -> |
||||||
|
stmt.executeQuery().use { rows -> |
||||||
|
while (rows.next()) { |
||||||
|
val group = rows.getInt(1) |
||||||
|
val checksum = rows.getInt(3) |
||||||
|
val version = rows.getInt(4) and 0xFFFF |
||||||
|
|
||||||
|
Unpooled.wrappedBuffer(rows.getBytes(2)).use { buf -> |
||||||
|
val actualVersion = VersionTrailer.peek(buf) |
||||||
|
if (actualVersion != version) { |
||||||
|
throw StoreCorruptException( |
||||||
|
"Group corrupt (expected version $version, actual version $actualVersion)" |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
val actualChecksum = buf.slice(buf.readerIndex(), buf.writerIndex() - 2).crc32() |
||||||
|
if (actualChecksum != checksum) { |
||||||
|
throw StoreCorruptException( |
||||||
|
"Group corrupt (expected checksum $checksum, actual checksum $actualChecksum)" |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
output.write(archive, group, buf) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue