import { describe, expect, it } from "bun:test" import * as Tbl from "../src/Tbl.ts" import * as TblText from "../src/TblText.ts" import { show, succeed } from "./helpers.ts" const repo = `${import.meta.dir}/../../..` const FIXTURES = [ "mpq/arr/images.tbl", "mpq/arr/portdata.tbl", "mpq/rez/gluHist.tbl", "mpq/rez/help_txt.tbl", "mpq/rez/network.tbl", "mpq/rez/stat_txt.tbl", "mpq/rez/tips.tbl", "mpq/rez/unitnames.tbl" ] describe("round trip against the shipped game data", () => { for (const fixture of FIXTURES) { it(`${fixture} survives .tbl -> .tbl.txt -> .tbl byte for byte`, async () => { const original = new Uint8Array(await Bun.file(`${repo}/${fixture}`).arrayBuffer()) const table = await succeed(Tbl.decode(original)) const text = TblText.encode(table) const reparsed = await succeed(TblText.decode(text)) const rebuilt = await succeed(Tbl.encode(reparsed)) expect(rebuilt.length).toBe(original.length) expect(rebuilt).toEqual(original) }) it(`${fixture} decompiles to pure ASCII`, async () => { const original = new Uint8Array(await Bun.file(`${repo}/${fixture}`).arrayBuffer()) const text = TblText.encode(await succeed(Tbl.decode(original))) const offender = [...text].find((char) => char.charCodeAt(0) > 0x7e) expect(offender).toBeUndefined() }) } it("renders images.tbl as plain, unescaped GRP paths", async () => { const original = new Uint8Array(await Bun.file(`${repo}/mpq/arr/images.tbl`).arrayBuffer()) const table = await succeed(Tbl.decode(original)) const lines = TblText.encode(table, { header: false }).split("\n") expect(lines[0]).toBe("zerg\\avenger.grp") expect(lines[1]).toBe("protoss\\mshield.los") }) it("renders unitnames.tbl multi-part entries with <0> separators", async () => { const original = new Uint8Array(await Bun.file(`${repo}/mpq/rez/unitnames.tbl`).arrayBuffer()) const table = await succeed(Tbl.decode(original)) const lines = TblText.encode(table, { header: false }).split("\n") expect(lines[0]).toBe("Terran Maverick<0>*<0>Ground Units") expect(lines[5]).toBe("Terran Phalanx<0>Tank Mode<0>Ground Units") }) it("renders stat_txt.tbl colour codes and newlines as ", async () => { const original = new Uint8Array(await Bun.file(`${repo}/mpq/rez/stat_txt.tbl`).arrayBuffer()) const table = await succeed(Tbl.decode(original)) const line = TblText.encode(table, { header: false }).split("\n")[338]! expect(line).toBe( "e<0><4>Tank Mode <1>(<3>E<1>)<10>Returns the Phalanx's weapon and mobility to normal." ) }) it("dedupe reclaims stat_txt.tbl headroom without changing a single string", async () => { const original = new Uint8Array(await Bun.file(`${repo}/mpq/rez/stat_txt.tbl`).arrayBuffer()) const table = await succeed(Tbl.decode(original)) const deduped = await succeed(Tbl.encode(table, { dedupe: true })) expect(deduped.length).toBeLessThan(original.length) expect(show(await succeed(Tbl.decode(deduped)))).toEqual(show(table)) }) it("reports how little offset headroom stat_txt.tbl has left", async () => { const original = new Uint8Array(await Bun.file(`${repo}/mpq/rez/stat_txt.tbl`).arrayBuffer()) const view = new DataView(original.buffer) const count = view.getUint16(0, true) let highest = 0 for (let i = 0; i < count; i++) highest = Math.max(highest, view.getUint16(2 + i * 2, true)) expect(highest).toBeLessThanOrEqual(Tbl.MAX_OFFSET) expect(Tbl.MAX_OFFSET - highest).toBeLessThan(64) // 26 bytes at time of writing }) })