forked from openrs2/openrs2
Signed-off-by: Graham <gpe@openrs2.org>master
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