Skip to content

Instantly share code, notes, and snippets.

@snuffyDev
Created June 9, 2023 04:32
Show Gist options
  • Select an option

  • Save snuffyDev/a5e970708c7a1c2fbbead92fcbc858de to your computer and use it in GitHub Desktop.

Select an option

Save snuffyDev/a5e970708c7a1c2fbbead92fcbc858de to your computer and use it in GitHub Desktop.
// adapted from: https://github.com/vpoupet/wolfenstein/blob/master/js/files.js
class GameData {
private declare VSWAP: DataView;
private declare MAPHEAD: ArrayBuffer;
private declare GAMEMAPS: ArrayBuffer;
private declare wallTexturesOffset: number;
private declare plane0: Uint16Array;
private declare plane1: Uint16Array;
private declare plane2: boolean[][];
constructor() {
this.VSWAP = new DataView(new ArrayBuffer(0));
this.MAPHEAD = new Uint8Array(this.VSWAP.buffer);
this.GAMEMAPS = new Uint8Array(this.VSWAP.buffer);
this.wallTexturesOffset = 0;
this.plane0 = new Uint16Array(this.VSWAP.buffer);
this.plane1 = new Uint16Array(this.VSWAP.buffer);
this.plane2 = [];
}
get() {
return [
this.VSWAP,
this.MAPHEAD,
this.GAMEMAPS,
this.wallTexturesOffset,
this.plane0,
this.plane1,
this.plane2
] as const;
}
private rlewDecode(inView: Uint16Array): Uint16Array {
const mapHeadView = new Uint16Array(this.MAPHEAD);
const rlewTag = mapHeadView[0];
const size = inView[0];
const outView = new Uint16Array(size);
let inOffset = 2;
let outOffset = 0;
for (; inOffset < inView.length; inOffset++) {
const w = inView[inOffset];
if (w === rlewTag) {
const n = inView[++inOffset];
const x = inView[++inOffset];
for (let i = 0; i < n; i++) {
outView[outOffset++] = x;
}
} else {
outView[outOffset++] = w;
}
}
return outView;
}
private carmackDecode(inView: Uint8Array): Uint16Array {
const size = inView[0] | (inView[1] << 8);
const outView = new Uint16Array(size);
let inOffset = 2;
let outOffset = 0;
while (inOffset < inView.length) {
const x = inView[inOffset + 1];
if (x === 0xa7 || x === 0xa8) {
const n = inView[inOffset];
if (n === 0) {
outView[outOffset++] = inView[inOffset + 2] | (x << 8);
inOffset += 3;
} else if (x === 0xa7) {
const offset = 2 * inView[inOffset + 2];
for (let i = 0; i < n; i++) {
outView[outOffset++] = outView[outOffset - offset];
}
inOffset += 3;
} else {
const offset = 2 * (inView[inOffset + 2] | (inView[inOffset + 3] << 8));
for (let i = 0; i < n; i++) {
outView[outOffset++] = outView[offset + 2 * i];
}
inOffset += 4;
}
} else {
outView[outOffset++] = inView[inOffset] | (inView[inOffset + 1] << 8);
inOffset += 2;
}
}
return outView;
}
loadBytes(url: string): Promise<ArrayBuffer> {
return fetch(url).then((response) => {
if (!response.ok) {
throw new Error(`Failed to load ${url}: ${response.status} ${response.statusText}`);
}
return response.arrayBuffer();
});
}
loadResources(): Promise<void | void[]> {
const splashPromise = new Promise<void>((resolve) => setTimeout(resolve, 1000));
const gamemapsPromise = this.loadBytes("data/GAMEMAPS.WL6");
const mapheadPromise = this.loadBytes("data/MAPHEAD.WL6");
const vswapPromise = this.loadBytes("data/VSWAP.WL6");
return Promise.all([splashPromise, gamemapsPromise, mapheadPromise, vswapPromise]).then(
([_, gamemaps, maphead, vswap]) => {
this.GAMEMAPS = gamemaps;
this.MAPHEAD = maphead;
this.VSWAP = new DataView(vswap);
this.wallTexturesOffset = this.VSWAP.getUint32(6, true);
}
);
}
loadLevel(level: number): void {
const mapHeadView = new DataView(this.MAPHEAD);
const offset = mapHeadView.getUint32(2 + 4 * level, true);
const mapHeader = new DataView(this.GAMEMAPS, offset, 42);
const plane0Offset = mapHeader.getUint32(0, true);
const plane0Size = mapHeader.getUint16(12, true);
const plane1Offset = mapHeader.getUint32(4, true);
const plane1Size = mapHeader.getUint16(14, true);
this.plane0 = this.rlewDecode(
this.carmackDecode(new Uint8Array(this.GAMEMAPS, plane0Offset, plane0Size))
);
this.plane1 = this.rlewDecode(
this.carmackDecode(new Uint8Array(this.GAMEMAPS, plane1Offset, plane1Size))
);
this.plane2 = Array.from({ length: 64 }, () => Array(64).fill(false));
const plane2View = new DataView(this.GAMEMAPS, offset + 6, (64 * 64) / 8);
for (let i = 0; i < (64 * 64) / 8; i++) {
const byte = plane2View.getUint8(i);
for (let j = 0; j < 8; j++) {
const index = i * 8 + j;
const row = Math.floor(index / 64);
const col = index % 64;
this.plane2[row][col] = (byte & (1 << (7 - j))) !== 0;
}
}
}
getMap0(x: number, y: number): number {
return this.plane0[y * 64 + x];
}
setMap0(x: number, y: number, value: number): void {
this.plane0[y * 64 + x] = value;
}
getMap1(x: number, y: number): number {
return this.plane1[y * 64 + x];
}
setMap1(x: number, y: number, value: number): void {
this.plane1[y * 64 + x] = value;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment