forked from openrs2/openrs2
Signed-off-by: Graham <gpe@openrs2.org>
parent
8c415023af
commit
aa2784a9e6
@ -0,0 +1,162 @@ |
|||||||
|
package org.openrs2.cache |
||||||
|
|
||||||
|
import io.netty.buffer.ByteBufAllocator |
||||||
|
import io.netty.buffer.Unpooled |
||||||
|
import org.openrs2.buffer.crc32 |
||||||
|
import org.openrs2.buffer.use |
||||||
|
import java.nio.file.Files |
||||||
|
import java.nio.file.Path |
||||||
|
import java.util.Base64 |
||||||
|
import javax.inject.Inject |
||||||
|
import javax.inject.Singleton |
||||||
|
import kotlin.io.path.name |
||||||
|
|
||||||
|
@Singleton |
||||||
|
public class RuneLiteStore @Inject constructor( |
||||||
|
private val alloc: ByteBufAllocator |
||||||
|
) { |
||||||
|
public fun unpack(input: Path, output: Store) { |
||||||
|
output.create(Store.ARCHIVESET) |
||||||
|
|
||||||
|
for (path in Files.list(input)) { |
||||||
|
val name = path.name |
||||||
|
if (!name.endsWith(".flatcache")) { |
||||||
|
continue |
||||||
|
} |
||||||
|
|
||||||
|
val archive = name.removeSuffix(".flatcache").toIntOrNull() ?: continue |
||||||
|
unpackArchive(path, archive, output) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun unpackArchive(path: Path, archive: Int, output: Store) { |
||||||
|
val index = Js5Index(Js5Protocol.ORIGINAL) |
||||||
|
var indexChecksum = 0 |
||||||
|
|
||||||
|
Files.newBufferedReader(path).useLines { lines -> |
||||||
|
var group: Js5Index.MutableGroup? = null |
||||||
|
|
||||||
|
for (line in lines) { |
||||||
|
val pair = line.split('=', limit = 2) |
||||||
|
if (pair.size != 2) { |
||||||
|
throw StoreCorruptException("Missing = in line") |
||||||
|
} |
||||||
|
|
||||||
|
val (key, value) = pair |
||||||
|
|
||||||
|
if (group == null) { |
||||||
|
when (key) { |
||||||
|
"protocol" -> { |
||||||
|
val protocolId = value.toIntOrNull() |
||||||
|
?: throw StoreCorruptException("Protocol must be an integer") |
||||||
|
|
||||||
|
index.protocol = Js5Protocol.fromId(protocolId) |
||||||
|
?: throw StoreCorruptException("Protocol number not supported") |
||||||
|
} |
||||||
|
|
||||||
|
"revision" -> { |
||||||
|
index.version = value.toIntOrNull() |
||||||
|
?: throw StoreCorruptException("Revision must be an integer") |
||||||
|
} |
||||||
|
|
||||||
|
"compression" -> Unit |
||||||
|
|
||||||
|
"crc" -> { |
||||||
|
indexChecksum = value.toIntOrNull() |
||||||
|
?: throw StoreCorruptException("Index CRC must be an integer") |
||||||
|
} |
||||||
|
|
||||||
|
"named" -> Unit |
||||||
|
|
||||||
|
"id" -> { |
||||||
|
val id = value.toIntOrNull() |
||||||
|
?: throw StoreCorruptException("Group ID must be an integer") |
||||||
|
|
||||||
|
group = index.createOrGet(id) |
||||||
|
} |
||||||
|
|
||||||
|
else -> throw StoreCorruptException("Unknown key in archive context: $key") |
||||||
|
} |
||||||
|
} else { |
||||||
|
when (key) { |
||||||
|
"namehash" -> { |
||||||
|
group.nameHash = value.toIntOrNull() |
||||||
|
?: throw StoreCorruptException("Group name hash must be an integer") |
||||||
|
|
||||||
|
if (group.nameHash != 0) { |
||||||
|
index.hasNames = true |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
"revision" -> { |
||||||
|
group.version = value.toIntOrNull() |
||||||
|
?: throw StoreCorruptException("Revision must be an integer") |
||||||
|
} |
||||||
|
|
||||||
|
"crc" -> { |
||||||
|
group.checksum = value.toIntOrNull() |
||||||
|
?: throw StoreCorruptException("Group CRC must be an integer") |
||||||
|
} |
||||||
|
|
||||||
|
"contents" -> { |
||||||
|
Unpooled.wrappedBuffer(Base64.getDecoder().decode(value)).use { buf -> |
||||||
|
output.write(archive, group!!.id, buf) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
"compression" -> Unit |
||||||
|
|
||||||
|
"file" -> { |
||||||
|
val pair = value.split('=', limit = 2) |
||||||
|
if (pair.size != 2) { |
||||||
|
throw StoreCorruptException("Missing = in file line") |
||||||
|
} |
||||||
|
|
||||||
|
val id = pair[0].toIntOrNull() |
||||||
|
?: throw StoreCorruptException("File ID must be an integer") |
||||||
|
|
||||||
|
val file = group.createOrGet(id) |
||||||
|
|
||||||
|
file.nameHash = pair[1].toIntOrNull() |
||||||
|
?: throw StoreCorruptException("File name hash must be an integer") |
||||||
|
|
||||||
|
if (file.nameHash != 0) { |
||||||
|
index.hasNames = true |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
"id" -> { |
||||||
|
val id = value.toIntOrNull() |
||||||
|
?: throw StoreCorruptException("Group ID must be an integer") |
||||||
|
|
||||||
|
group = index.createOrGet(id) |
||||||
|
} |
||||||
|
|
||||||
|
else -> throw StoreCorruptException("Unknown key in group context: $key") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
alloc.buffer().use { uncompressed -> |
||||||
|
index.write(uncompressed) |
||||||
|
|
||||||
|
val matching = Js5CompressionType.values().count { type -> |
||||||
|
Js5Compression.compress(uncompressed.slice(), type).use { compressed -> |
||||||
|
val checksum = compressed.crc32() |
||||||
|
|
||||||
|
if (checksum == indexChecksum) { |
||||||
|
output.write(Store.ARCHIVESET, archive, compressed) |
||||||
|
return@use true |
||||||
|
} |
||||||
|
|
||||||
|
return@use false |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (matching != 1) { |
||||||
|
throw StoreCorruptException("Failed to reconstruct Js5Index") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue