@abextm/cache2
Version:
Utilities for reading OSRS "caches"
201 lines (200 loc) • 6.73 kB
JavaScript
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,
};
}
}