60 lines
2.8 KiB
TypeScript
60 lines
2.8 KiB
TypeScript
/**
|
|
* Construction check for the TBL decoder.
|
|
*
|
|
* The classic TBL layout has no independent decoder available (the community Go
|
|
* package implements the later hash-table variant), so this is a two-sided test
|
|
* rather than a differential one: the script *writes* a table whose entries are
|
|
* known — including empty strings, unused indices and non-ASCII text — and then
|
|
* requires the decoder to reproduce exactly that. It catches layout and
|
|
* decoding mistakes (off-by-one offsets, byte/character confusion, UTF-16
|
|
* endianness) but cannot catch a wrong reading shared by writer and reader;
|
|
* that is what a real `string.tbl` is for.
|
|
*
|
|
* Usage: node scripts/verify-tbl.ts
|
|
*/
|
|
import { decodeTbl, tblLookup } from '../src/formats/tbl.ts'
|
|
import { encodeTbl } from './lib/tbl-writer.ts'
|
|
import type { TblEntry } from './lib/tbl-writer.ts'
|
|
|
|
/** Entries the decoder must reproduce, in order. */
|
|
const entries: TblEntry[] = [
|
|
'Stamina Potion', // plain ASCII
|
|
'', // empty string is a value, not a hole
|
|
null, // unused index
|
|
'Tome of Town Portal',
|
|
null,
|
|
'完美宝石', // CJK: multi-byte UTF-16, but one unit each
|
|
'Straße', // Latin-1 range
|
|
null,
|
|
'🌀 Emoji beyond the BMP', // surrogate pair: two units for one character
|
|
'x'.repeat(300), // long entry
|
|
]
|
|
|
|
const encoded = encodeTbl(entries)
|
|
const decoded = decodeTbl(encoded)
|
|
const lookup = tblLookup(decoded)
|
|
|
|
const problems: string[] = []
|
|
if (encoded.byteLength !== decoded.length * 0 + encoded.byteLength) {
|
|
/* v8 ignore next -- placeholder to keep the shape obvious. */
|
|
problems.push('unreachable')
|
|
}
|
|
if (decoded.length !== entries.length) problems.push(`entry count ${String(decoded.length)} != ${String(entries.length)}`)
|
|
entries.forEach((want, index) => {
|
|
const got = decoded[index]
|
|
if (want === null) {
|
|
if (got !== undefined) problems.push(`index ${String(index)}: expected an unused index, got ${JSON.stringify(got)}`)
|
|
return
|
|
}
|
|
if (got !== want) problems.push(`index ${String(index)}: ${JSON.stringify(got)} != ${JSON.stringify(want)}`)
|
|
})
|
|
|
|
console.log(`table ${String(entries.length)} indices (${String(lookup.size)} used)`)
|
|
console.log(`encoded ${String(encoded.byteLength)} bytes`)
|
|
console.log(`unicode CJK=${JSON.stringify(lookup.get(5))} latin1=${JSON.stringify(lookup.get(6))} astral=${JSON.stringify(lookup.get(8))}`)
|
|
console.log(`long ${String(lookup.get(9)?.length ?? 0)} characters round-tripped`)
|
|
console.log(`problems ${String(problems.length)}`)
|
|
for (const problem of problems.slice(0, 8)) console.log(` - ${problem}`)
|
|
console.log(problems.length === 0 ? 'RESULT every index round-trips exactly' : 'RESULT FAILED')
|
|
process.exit(problems.length === 0 ? 0 : 1)
|