@abextm/cache2
Version:
Utilities for reading OSRS "caches"
202 lines (187 loc) • 4.68 kB
text/typescript
import { CacheVersion } from "./Cache.js";
import { ItemID, KitID, KitOrItem, ParamID, Params } from "./types.js";
export const cp1252CharMap: string[] = (() => {
const ext = "€?‚ƒ„…†‡ˆ‰Š‹Œ?Ž??‘’“”•–—˜™š›œ?žŸ";
let out = new Array(256);
for (let i = 0; i < out.length; i++) {
let v;
let ei = i - 128;
if (ei > 0 && ei < ext.length) {
v = ext.charAt(ei);
} else {
v = String.fromCodePoint(i);
}
out[i] = v;
}
return out;
})();
export class Reader {
public readonly view: DataView;
public offset = 0;
get length(): number {
return this.view.byteLength;
}
get remaining(): number {
return this.length - this.offset;
}
set remaining(v: number) {
this.offset = this.length - v;
}
public static makeViewOf<V>(
typ: new(buffer: ArrayBuffer, byteOffset: number, byteLength: number) => V,
view: ArrayBufferView | ArrayBuffer | DataView,
): V {
if (view instanceof typ) {
return view;
}
if (view instanceof ArrayBuffer) {
return new typ(view, 0, view.byteLength);
}
return new typ(view.buffer, view.byteOffset, view.byteLength);
}
public constructor(view: ArrayBufferView | ArrayBuffer, public version?: CacheVersion | undefined) {
this.view = Reader.makeViewOf(DataView, view);
}
private bump(delta: number): number {
let r = this.offset;
this.offset += delta;
return r;
}
public subreader(length: number): Reader {
let start = this.bump(length);
return new Reader(new DataView(this.view.buffer, this.view.byteOffset + start, length), this.version);
}
public array(length: number): Uint8Array {
let start = this.bump(length);
return new Uint8Array(this.view.buffer, this.view.byteOffset + start, length);
}
public isAfter(ver: CacheVersion): boolean {
return CacheVersion.isAfter(this.version, ver);
}
public u8(): number {
return this.view.getUint8(this.bump(1));
}
public u8p1(): number {
return this.u8() + 1;
}
public i8(): number {
return this.view.getInt8(this.bump(1));
}
public u16(): number {
return this.view.getUint16(this.bump(2));
}
public u16n(): number {
let v = this.view.getUint16(this.bump(2));
return v == 0xFFFF ? -1 : v;
}
public i16(): number {
return this.view.getInt16(this.bump(2));
}
public u8o16(): number { // rl getUSmart
if (this.view.getUint8(this.offset) < 128) {
return this.u8();
} else {
return this.u16() - 0x8000;
}
}
public u8o16m1(): number { // rl readUnsignedShortSmartMinusOne
if (this.view.getUint8(this.offset) < 128) {
return this.u8() - 1;
} else {
return this.u16() - 0x8001;
}
}
public u24(): number {
let off = this.bump(3);
return (this.view.getUint8(off) << 16) | this.view.getUint16(off + 1);
}
public i32(): number {
return this.view.getInt32(this.bump(4));
}
public s2o4n(): number { // rl BigSmart2
if (this.view.getUint8(this.offset) & 0x80) {
return this.i32() & (-1 >>> 1);
} else {
return this.u16n();
}
}
public i64(): bigint {
return this.view.getBigInt64(this.bump(8));
}
public string(): string {
let s = "";
for (let c: number; (c = this.u8()) != 0;) {
s += cp1252CharMap[c];
}
return s;
}
public vString(): string {
if (this.u8() != 0) {
throw new Error("invalid string");
}
return this.string();
}
public stringNullHidden(): string | null {
let s = this.string();
if (s == "hidden") {
return null;
}
return s;
}
public params(): Params {
let count = this.u8();
let out = new Params();
for (let i = 0; i < count; i++) {
let type = this.u8();
let param = this.u24();
switch (type) {
case 0:
out.set(param as ParamID, this.i32());
break;
case 1:
out.set(param as ParamID, this.string());
break;
default:
throw new Error(`invalid type in param table ${type}`);
}
}
return out;
}
public kit(): KitOrItem {
let id = this.u16();
if (id === 0) {
return undefined;
} else if (id >= 512) {
return { item: (id - 512) as ItemID };
} else if (id >= 256) {
return { kit: (id - 256) as KitID };
} else {
throw new Error(`invalid KitOrItem ${id}`);
}
}
public u32o16(): number { // rl BigSmart
if (this.view.getUint8(this.offset) & 0x80) {
return this.i32() & (-1 >>> 1);
} else {
return this.u16();
}
}
public u32o16n(): number { // rl BigSmart2
if (this.view.getUint8(this.offset) & 0x80) {
return this.i32() & (-1 >>> 1);
} else {
return this.u16n();
}
}
public leVarInt(): number { // rl LEVarInt
let v = 0;
let shift = 0;
let octet;
do {
octet = this.u8();
v |= (octet & 127) << shift;
shift += 7;
} while (octet > 127);
return v;
}
}