import assert from "node:assert/strict"; import { deflateSync } from "node:zlib"; import test from "node:test"; import { dxt1ToRgba, encodeChunkKey, intersectingChunkIndices, parseTerrainChunk, readDataStore, wrapDxt1AsDds, } from "./extract-native-terrain.mjs"; function i32(value) { const buffer = Buffer.alloc(4); buffer.writeInt32LE(value); return buffer; } function u32(value) { const buffer = Buffer.alloc(4); buffer.writeUInt32LE(value); return buffer; } function f32(value) { const buffer = Buffer.alloc(4); buffer.writeFloatLE(value); return buffer; } function makeChunk({ dimension = 2, visibleVertices = null, waterLayerCount = 0, waterPayload = Buffer.alloc(0) } = {}) { const count = (dimension + 1) ** 2 + (dimension + 1) * 4; const classGuid = Buffer.alloc(16); const heights = Buffer.concat(Array.from({ length: count }, (_, index) => f32(index / 10))); const vertexData = Buffer.concat(Array.from({ length: count }, (_, index) => { const visible = visibleVertices === null || visibleVertices.has(index); const alpha = visible ? 255 : 0; return u32(((alpha << 24) | (191 << 16) | (128 << 8) | 255) >>> 0); })); const bounds = Buffer.concat([ f32(-960), f32(-2), f32(-960), f32(-945), f32(2), f32(-945), ]); const mapDxt1 = Buffer.alloc(8); mapDxt1.writeUInt16LE(0xffff, 0); mapDxt1.writeUInt16LE(0x0000, 2); const layerIds = Buffer.concat([u32(1).subarray(0, 2), u32(3).subarray(0, 2), u32(9).subarray(0, 2)]); const bytes = Buffer.concat([ classGuid, i32(4), // chunk iteration i32(dimension), f32(7.5), i32(4), bounds, f32(-960), f32(-960), heights, vertexData, i32(128), // splat resolution classGuid, i32(1), // compressed-alpha map type i32(4), // splat map dimension i32(3), layerIds, i32(1), // alpha maps i32(1), // mip count u32(4), u32(4), mapDxt1, i32(0), // doodad clusters i32(waterLayerCount), // water layers waterPayload, ]); return deflateSync(bytes); } function makeDataStoreFixture() { const tablePosition = 28; const recordHeaderPosition = tablePosition + 272; const recordPayloadPosition = recordHeaderPosition + 16; const payload = Buffer.from([1, 2, 3, 4]); const buffer = Buffer.alloc(recordPayloadPosition + payload.length); buffer.writeInt32LE(tablePosition, 16); buffer.writeInt32LE(1, 20); buffer.writeInt32LE(32, 24); buffer.write("header", tablePosition, "ascii"); buffer.writeInt32LE(4, tablePosition + 256); buffer.writeInt32LE(recordHeaderPosition, tablePosition + 260); buffer.writeInt32LE(1, tablePosition + 264); buffer.writeInt32LE(64, tablePosition + 268); buffer.writeUInt32LE(0, recordHeaderPosition); buffer.writeInt32LE(recordPayloadPosition, recordHeaderPosition + 4); buffer.writeInt32LE(payload.length, recordHeaderPosition + 8); buffer.writeInt32LE(64, recordHeaderPosition + 12); payload.copy(buffer, recordPayloadPosition); return buffer; } test("terrain chunk parser preserves source geometry, visibility, normals, and splat layer IDs", () => { const parsed = parseTerrainChunk(makeChunk({ visibleVertices: new Set([0]) }), "test"); assert.equal(parsed.dimension, 2); assert.equal(parsed.localPositions.length, 27); assert.deepEqual([...parsed.normalizedUvs.slice(0, 6)], [0, 0, 0.5, 0, 1, 0]); assert.deepEqual([...parsed.layerIds], [1, 3, 9]); assert.deepEqual([...parsed.triangleVisibility], [1, 1, 0, 0, 0, 0, 0, 0]); assert.deepEqual([...parsed.indices], [0, 3, 4, 0, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); assert.deepEqual([...parsed.cellVisibility], [1, 0, 0, 0]); const expectedNormal = [ (191 / 255 - 0.5) * 2, (128 / 255 - 0.5) * 2, 1, ]; for (let axis = 0; axis < 3; axis += 1) { assert.ok(Math.abs(parsed.nativeNormals[axis] - expectedNormal[axis]) < 1e-6); } assert.equal(parsed.splatMaps[0].mipmaps[0].bytes.length, 8); assert.equal(parsed.doodadClusterCount, 0); assert.equal(parsed.waterLayerCount, 0); assert.equal(parsed.unconsumedBytes, 0); }); test("terrain topology retains every face slot and degenerates fully masked triangles", () => { const parsed = parseTerrainChunk(makeChunk({ visibleVertices: new Set() }), "masked-test"); assert.equal(parsed.indices.length / 3, 8); assert.equal(parsed.triangleVisibility.reduce((sum, value) => sum + value, 0), 0); assert.ok(parsed.indices.every((value) => value === 0)); assert.ok(parsed.cellVisibility.every((value) => value === 0)); }); test("water-bearing WDB chunks can export their unchanged ground heightfield when explicitly enabled", () => { const source = makeChunk({ waterLayerCount: 1, waterPayload: Buffer.from([0x51, 0x52, 0x53, 0x54]) }); assert.throws(() => parseTerrainChunk(source, "wet-fixture"), /allowUnparsedWaterPayload/); const parsed = parseTerrainChunk(source, "wet-fixture", { allowUnparsedWaterPayload: true }); assert.equal(parsed.waterLayerCount, 1); assert.equal(parsed.heights[0], 0); assert.deepEqual([...parsed.triangleVisibility], [1, 1, 1, 1, 1, 1, 1, 1]); assert.equal(parsed.unconsumedBytes, 4); }); test("Rune disk datastore table and record headers are read using native offsets", () => { const datastore = readDataStore(makeDataStoreFixture()); const header = datastore.tables.get("header"); const record = header.records.get("0"); assert.equal(datastore.tableCount, 1); assert.equal(header.keyLength, 4); assert.equal(header.count, 1); assert.equal(record.position, 316); assert.deepEqual([...record.key], [0, 0, 0, 0]); }); test("native terrain chunk selection and DXT1 standalone DDS wrapper are deterministic", () => { const selected = intersectingChunkIndices({ origin: [-7680, 0, -7680], chunkDimension: 64, gridSize: 7.5, numXChunks: 32, numZChunks: 32, }, { min: [-664.98, 0, -716.26], max: [180.76, 0, 117.06] }); const dxt1 = Buffer.alloc(8); dxt1.writeUInt16LE(0xffff, 0); const rgba = dxt1ToRgba(dxt1, 4, 4); const dds = wrapDxt1AsDds(4, 4, dxt1); assert.equal(selected.length, 9); assert.equal(selected[0].x, 14); assert.equal(selected[0].z, 14); assert.equal(encodeChunkKey(14, 14), 917518); assert.deepEqual([...rgba.subarray(0, 4)], [255, 255, 255, 255]); assert.equal(dds.toString("ascii", 0, 4), "DDS "); assert.equal(dds.readUInt32LE(12), 4); assert.equal(dds.readUInt32LE(16), 4); assert.equal(dds.toString("ascii", 84, 88), "DXT1"); assert.deepEqual(dds.subarray(128), dxt1); const rowPattern = Buffer.alloc(8); rowPattern.writeUInt16LE(0xf800, 0); // red endpoint rowPattern.writeUInt16LE(0x001f, 2); // blue endpoint let packed = 0; for (let pixel = 0; pixel < 16; pixel += 1) { packed = (packed | ((pixel >>> 2) << (pixel * 2))) >>> 0; } rowPattern.writeUInt32LE(packed, 4); const rowPixels = dxt1ToRgba(rowPattern, 4, 4); assert.deepEqual([...rowPixels.subarray(0, 4)], [255, 0, 0, 255]); assert.deepEqual([...rowPixels.subarray(4 * 4, 4 * 4 + 4)], [0, 0, 255, 255]); });