diablo2-web/scripts/verify-dt1-pixels.ts

52 lines
2.4 KiB
TypeScript

/**
* Pixel-level check for the DT1 decoder.
*
* The Go reference package parses DT1 headers correctly but never calls its own
* graphics decoder, so it cannot serve as a pixel oracle. This closes that gap
* from the other side: the fixture generator writes down the grid it encoded,
* and the decoder must reproduce it pixel for pixel — which validates the
* placement logic (skip runs, row advance, isometric tables, y-offset) rather
* than merely "did not throw".
*
* Usage: node scripts/verify-dt1-pixels.ts <fixture-directory>
*/
import { readFile } from 'node:fs/promises'
import { join } from 'node:path'
import { decodeDt1 } from '../src/formats/dt1.ts'
interface Expected {
readonly width: number
readonly height: number
readonly tiles: readonly { readonly format: number; readonly base: number; readonly pixels: readonly number[] }[]
}
const dir = process.argv[2]
if (dir === undefined) {
console.error('usage: node scripts/verify-dt1-pixels.ts <fixture-directory>')
process.exit(2)
}
const expected = JSON.parse(await readFile(join(dir, 'expected-dt1.json'), 'utf8')) as Expected
const library = decodeDt1(new Uint8Array(await readFile(join(dir, 'fixture.dt1'))))
const problems: string[] = []
expected.tiles.forEach((want, index) => {
const tile = library.tiles[index]
if (tile === undefined) { problems.push(`tile ${String(index)} missing`); return }
const block = tile.blocks[0]
if (block === undefined) { problems.push(`tile ${String(index)} has no blocks`); return }
if (block.format !== want.format) problems.push(`tile ${String(index)} format ${String(block.format)} != ${String(want.format)}`)
let mismatched = 0
for (let at = 0; at < want.pixels.length; at += 1) {
if ((block.pixels[at] ?? 0) !== want.pixels[at]) mismatched += 1
}
if (mismatched > 0) problems.push(`tile ${String(index)}: ${String(mismatched)} of ${String(want.pixels.length)} pixels differ`)
})
console.log(`fixture ${join(dir, 'fixture.dt1')}`)
console.log(`tiles ${String(library.tiles.length)}`)
console.log(`pixels ${String(expected.tiles.reduce((n, t) => n + t.pixels.length, 0))} compared`)
console.log(`mismatches ${String(problems.length)}`)
for (const problem of problems.slice(0, 8)) console.log(` - ${problem}`)
console.log(problems.length === 0 ? 'RESULT every pixel matches the encoded pattern' : 'RESULT FAILED')
process.exit(problems.length === 0 ? 0 : 1)