UNPKG

@abextm/cache2

Version:

Utilities for reading OSRS "caches"

183 lines (182 loc) 5.79 kB
import { Loadable, PerFileLoadable } from "../Loadable.js"; import { Reader } from "../Reader.js"; import { Typed } from "../reflect.js"; import { BaseVarType, ScriptVarType } from "../ScriptVarType.js"; import { DBColumnID } from "../types.js"; function readTypes(r) { let size = r.u8(); let types = new Array(size).fill(undefined); for (let i = 0; i < size; i++) { types[i] = r.u8o16(); } return types; } function readVarType(r, type) { switch (type) { case "int": return r.i32(); break; case "string": return r.string(); break; case "long": return r.i64(); break; default: throw new Error(`unknown BaseVarType ${type}`); } } function readValues(r, types) { let strides = r.u8o16(); let values = new Array(types.length * strides).fill(undefined); for (let stride = 0; stride < strides; stride++) { for (let i = 0; i < types.length; i++) { let type = ScriptVarType.forID(types[i]); if (type === undefined) { throw new Error(`unknown type ${types[i]}`); } values[stride * types.length + i] = readVarType(r, type.baseType); } } return values; } export class DBRow extends PerFileLoadable { id; constructor(id) { super(); this.id = id; } static index = 2; static archive = 38; static gameval = 9; table = -1; values = []; types = []; static decode(r, id) { const v = new DBRow(id); for (let opcode; (opcode = r.u8()) != 0;) { switch (opcode) { case 3: { let len = r.u8(); v.values = new Array(len).fill(undefined); v.types = new Array(len).fill(undefined); for (;;) { let column = r.u8(); if (column == 255) { break; } let types = v.types[column] = readTypes(r); v.values[column] = readValues(r, types); } break; } case 4: v.table = r.leVarInt(); break; default: throw new Error(`unknown opcode ${opcode}`); } } return v; } } export class DBTable extends PerFileLoadable { id; constructor(id) { super(); this.id = id; } static index = 2; static archive = 39; static gameval = 10; types = []; defaultValues = []; static decode(r, id) { const v = new DBTable(id); for (let opcode; (opcode = r.u8()) != 0;) { switch (opcode) { case 1: { let len = r.u8(); v.defaultValues = new Array(len).fill(undefined); v.types = new Array(len).fill(undefined); for (;;) { let bits = r.u8(); if (bits == 255) { break; } let column = bits & 0x7F; let types = v.types[column] = readTypes(r); if (bits & 0x80) { v.defaultValues[column] = readValues(r, types); } } break; } default: throw new Error(`unknown opcode ${opcode}`); } } return v; } static async loadRowIDs(cache, table) { let idx = await DBTableIndex.load(cache, table, DBTableIndex.MASTER_COLUMN); let vs = idx?.values[0]; if (!vs) { return undefined; } return vs.get(0) ?? []; } static async loadRows(cache, table) { let rows = await this.loadRowIDs(cache, table); if (!rows) { return undefined; } return await Promise.all(rows.map(id => DBRow.load(cache, id))); } } export class DBTableIndex extends Loadable { id; column; constructor(id, column) { super(); this.id = id; this.column = column; } static index = 21; static MASTER_COLUMN = -1; types = []; values = []; static async loadData(cache, ...args) { if (args.length == 1) { args = DBColumnID.unpack(args[0]); } let archive = await cache.getArchive(this.index, args[0]); let version = await cache.getVersion(this.index); let data = archive?.getFile(args[1] + 1)?.data; return data ? new Reader(data, version) : undefined; } static decode(r, ...args) { if (args.length == 1) { args = DBColumnID.unpack(args[0]); } const v = new DBTableIndex(args[0], args[1]); let len = r.leVarInt(); v.types = new Array(len).fill(undefined); v.values = new Array(len).fill(undefined); for (let tupleIndex = 0; tupleIndex < len; tupleIndex++) { let type = v.types[tupleIndex] = BaseVarType.forOrdinal(r.u8()); let map = v.values[tupleIndex] = new Map(); let numKeys = r.leVarInt(); for (let i = 0; i < numKeys; i++) { let key = readVarType(r, type); let numRows = r.leVarInt(); let rows = new Array(numRows).fill(undefined); map.set(key, rows); for (let i = 0; i < numRows; i++) { rows[i] = r.leVarInt(); } } } return v; } }