mdx-m3-viewer
Version:
A browser WebGL model viewer. Mainly focused on models of the games Warcraft 3 and Starcraft 2.
115 lines (87 loc) • 2.77 kB
text/typescript
import MpqCrypto from './crypto';
import MpqHash from './hash';
import { HASH_TABLE_KEY, HASH_TABLE_INDEX, HASH_NAME_A, HASH_NAME_B } from './constants';
/**
* A MPQ hash table.
*/
export default class MpqHashTable {
c: MpqCrypto;
entries: MpqHash[];
constructor(c: MpqCrypto) {
this.c = c;
this.entries = [];
// Minimum size
this.addEmpties(4);
}
clear() {
this.entries.length = 0;
}
addEmpties(howMany: number) {
for (let i = 0; i < howMany; i++) {
this.entries.push(new MpqHash());
}
}
getInsertionIndex(name: string) {
let entries = this.entries;
let offset = this.c.hash(name, HASH_TABLE_INDEX) & (entries.length - 1);
for (let i = 0, l = entries.length; i < l; i++) {
let index = (i + offset) % l;
let hash = entries[index];
if (hash.platform === 0xFFFF) {
return index;
}
}
return -1;
}
add(name: string, blockIndex: number) {
let insertionIndex = this.getInsertionIndex(name);
if (insertionIndex !== -1) {
let hash = this.entries[insertionIndex];
hash.nameA = this.c.hash(name, HASH_NAME_A);
hash.nameB = this.c.hash(name, HASH_NAME_B);
hash.locale = 0;
hash.platform = 0;
hash.blockIndex = blockIndex;
return hash;
}
}
load(typedArray: Uint8Array) {
let entriesCount = typedArray.byteLength / 16;
let uint32array = new Uint32Array(this.c.decryptBlock(typedArray, HASH_TABLE_KEY).buffer);
let offset = 0;
// Clear the table and add the needed empties.
this.clear();
this.addEmpties(entriesCount);
for (let hash of this.entries) {
hash.load(uint32array.subarray(offset, offset + 4));
offset += 4;
}
}
save(typedArray: Uint8Array) {
let uint32array = new Uint32Array(this.entries.length * 4);
let offset = 0;
for (let hash of this.entries) {
hash.save(uint32array.subarray(offset, offset + 4));
offset += 4;
}
let uint8array = new Uint8Array(uint32array.buffer);
this.c.encryptBlock(uint8array, HASH_TABLE_KEY);
typedArray.set(uint8array);
}
get(name: string) {
let c = this.c;
let entries = this.entries;
let offset = c.hash(name, HASH_TABLE_INDEX) & (entries.length - 1);
let nameA = c.hash(name, HASH_NAME_A);
let nameB = c.hash(name, HASH_NAME_B);
for (let i = 0, l = entries.length; i < l; i++) {
let hash = entries[(i + offset) % l];
if (nameA === hash.nameA && nameB === hash.nameB) {
return hash;
} else if (hash.blockIndex === 0xFFFFFFFF) {
return null;
}
}
return null;
}
}