UNPKG

@abextm/cache2

Version:

Utilities for reading OSRS "caches"

201 lines (200 loc) 6.73 kB
import { ArchiveData, hash } from "./Cache.js"; import { Reader } from "./Reader.js"; export class DiskIndexData { id; protocol; revision; compression; crc; named; sized; /** @internal */ archives = new Map(); } export class DiskCacheProvider { disk; data; indexData = new Map(); pointers = new Map(); constructor(disk) { this.disk = disk; this.data = disk.getFile("main_file_cache.dat2"); this.getPointers(255); } async getIndex(index) { let id = this.indexData.get(index); if (!id) { this.indexData.set(index, id = (async () => { let cdata = await this.getArchiveRaw(255, index); if (!cdata) { return undefined; } let ad = new ArchiveData(255, index); ad.addFile(0, 0); ad.compressedData = cdata; let r = new Reader(ad.getFile(0).data); let out = new DiskIndexData(); out.id = index; let protocol = out.protocol = r.u8(); out.revision = protocol >= 6 ? r.i32() : -1; let flags = r.u8(); let named = out.named = !!(flags & 1); let sized = out.sized = !!(flags & 4); if (flags & ~(1 | 4)) { throw new Error(`unsupported flags ${flags.toString(16)}`); } let numArchives = protocol <= 6 ? r.u16() : r.u32o16(); let ams = new Array(numArchives); for (let i = 0, id = 0; i < numArchives; i++) { id += protocol <= 6 ? r.u16() : r.u32o16(); let v = new ArchiveData(index, id); ams[i] = v; out.archives.set(id, v); } if (named) { for (let am of ams) { am.namehash = r.i32(); } } for (let am of ams) { am.crc = r.i32(); } if (sized) { for (let am of ams) { am.compressedSize = r.i32(); am.decompressedSize = r.i32(); } } for (let am of ams) { am.revision = r.i32(); } let numFileses = new Uint32Array(ams.length); for (let i = 0; i < numArchives; i++) { numFileses[i] = protocol <= 6 ? r.u16() : r.u32o16(); } for (let i = 0; i < numArchives; i++) { let am = ams[i]; let numFiles = numFileses[i]; let id = 0; for (let i = 0; i < numFiles; i++) { id += protocol <= 6 ? r.u16() : r.u32o16(); am.addFile(id, 0); } } if (named) { for (let am of ams) { for (let file of am.files.values()) { file.namehash = r.i32(); } } } return out; })()); } return id; } async getPointers(index) { let ptrs = this.pointers.get(index); if (!ptrs) { this.pointers.set(index, ptrs = (async () => { let data = await this.disk.getFile("main_file_cache.idx" + index); return data && new Reader(data); })()); } return ptrs; } async getArchives(index) { let idx = await this.getIndex(index); if (!idx) { return; } return Array.from(idx.archives.keys()); } async getArchive(index, archive) { let idx = await this.getIndex(index); if (!idx) { return; } let am = idx.archives.get(archive); if (!am) { return; } if (!am.compressedData) { let d = await this.getArchiveRaw(index, archive); if (!d) { return; } am.compressedData = d; } return am; } async getArchiveRaw(index, archive) { let ptrs = await this.getPointers(index); if (!ptrs) { return undefined; } ptrs.offset = archive * 6; if (ptrs.remaining <= 0) { return undefined; } let len = ptrs.u24(); let sector = ptrs.u24(); let data = await this.data; let r = new Reader(data); let headerSize = archive > 0xFFFF ? 10 : 8; const SECTOR_SIZE = 520; let fastRead = len + headerSize <= SECTOR_SIZE; let obuf = fastRead ? undefined : new Uint8Array(len); let readBytes = 0; let part = 0; for (; readBytes < len;) { r.offset = sector * SECTOR_SIZE; let readArchive = headerSize == 10 ? r.i32() : r.u16(); let readPart = r.u16(); let nextSector = r.u24(); let readIndex = r.u8(); if (readArchive != archive) { throw new Error(`corrupted; read archive ${readArchive} wanted ${archive}`); } if (readPart != part) { throw new Error(`corrupted; read part ${readPart} wanted ${part}`); } if (readIndex != index) { throw new Error(`corrupted; read index ${readIndex} wanted ${index}`); } let sa = r.array(Math.min(SECTOR_SIZE - headerSize, len - readBytes)); if (fastRead) { return sa; } obuf.set(sa, readBytes); readBytes += sa.length; sector = nextSector; part++; } return obuf; } async getArchiveByName(index, name) { let namehash = hash(name); let idx = await this.getIndex(index); if (!idx) { return; } for (let ar of idx.archives.values()) { if (ar.namehash === namehash) { if (!ar.compressedData) { let d = await this.getArchiveRaw(index, ar.archive); if (!d) { return; } ar.compressedData = d; } return ar; } } } async getVersion(index) { return { era: "osrs", indexRevision: (await this.getIndex(index))?.revision ?? 0, }; } }