84 lines
3.0 KiB
TypeScript
84 lines
3.0 KiB
TypeScript
/**
|
|
* AUDIT TOOL (read-only, scratch): enumerate the REAL DT1 tile inventory.
|
|
*
|
|
* For every LevelType listed in /tmp/dt1_list.txt, open the DT1 straight out of
|
|
* the MPQs and dump every tile record's (orientation, mainIndex, subIndex,
|
|
* rarityFrameIndex). This is the authoritative ground truth for which
|
|
* (style, sequence) pairs legally exist -- AutoMap.txt is only a proxy because
|
|
* it lists tiles that have an automap glyph, not the full tile set.
|
|
*
|
|
* DT1 tile record is 96 bytes (Paul Siramy layout, corroborated by the port's own
|
|
* src/formats/dt1.ts which reads subTileFlags at +40 and block pointers at +72/76/80):
|
|
* +20 int32 orientation (0 == floor)
|
|
* +24 int32 mainIndex (== "style")
|
|
* +28 int32 subIndex (== "sequence")
|
|
* +32 int32 rarityFrameIndex
|
|
*
|
|
* Output: TSV to stdout -> levelType, dt1File, orientation, mainIndex, subIndex, rarity
|
|
*/
|
|
import { readFileSync, writeFileSync } from 'node:fs'
|
|
import { MpqArchive } from '../src/mpq/archive.ts'
|
|
import { fileSource } from '../src/mpq/file-source.ts'
|
|
|
|
const D2 = '/usr/local/google/home/taodao/d2-data'
|
|
// Search order matters: Patch_D2 overrides base archives.
|
|
const archives = []
|
|
for (const name of ['Patch_D2.mpq', 'd2exp.mpq', 'd2data.mpq']) {
|
|
archives.push({ name, ar: await MpqArchive.open(await fileSource(`${D2}/${name}`)) })
|
|
}
|
|
|
|
const i32 = (b: Uint8Array, o: number): number =>
|
|
new DataView(b.buffer, b.byteOffset, b.byteLength).getInt32(o, true)
|
|
|
|
const lines = readFileSync('/tmp/dt1_list.txt', 'utf8').trim().split('\n')
|
|
const out: string[] = ['levelType\tdt1\torientation\tmainIndex\tsubIndex\trarity']
|
|
const missing: string[] = []
|
|
let totalTiles = 0
|
|
|
|
for (const line of lines) {
|
|
const [ltRaw, rel] = line.split('\t')
|
|
if (ltRaw === undefined || rel === undefined) continue
|
|
const internal = `data\\global\\tiles\\${rel.replace(/\//g, '\\')}`
|
|
|
|
let data: Uint8Array | undefined
|
|
let from = ''
|
|
for (const { name, ar } of archives) {
|
|
const f = ar.find(internal)
|
|
if (f !== undefined) {
|
|
data = await ar.read(f)
|
|
from = name
|
|
break
|
|
}
|
|
}
|
|
if (data === undefined) {
|
|
missing.push(`${ltRaw}\t${rel}`)
|
|
continue
|
|
}
|
|
|
|
const major = i32(data, 0)
|
|
const minor = i32(data, 4)
|
|
if (major !== 7 || minor !== 6) {
|
|
missing.push(`${ltRaw}\t${rel}\tBAD VERSION ${major}.${minor}`)
|
|
continue
|
|
}
|
|
const tileCount = i32(data, 268)
|
|
const tileStart = i32(data, 272)
|
|
for (let k = 0; k < tileCount; k += 1) {
|
|
const at = tileStart + k * 96
|
|
if (at + 96 > data.byteLength) break
|
|
out.push(
|
|
`${ltRaw}\t${rel}\t${i32(data, at + 20)}\t${i32(data, at + 24)}\t` +
|
|
`${i32(data, at + 28)}\t${i32(data, at + 32)}`,
|
|
)
|
|
totalTiles += 1
|
|
}
|
|
console.error(` [${from.padEnd(12)}] lt=${ltRaw.padStart(2)} ${rel} -> ${tileCount} tiles`)
|
|
}
|
|
|
|
writeFileSync('/tmp/dt1_tiles.tsv', out.join('\n') + '\n')
|
|
console.error(`\nwrote /tmp/dt1_tiles.tsv : ${totalTiles} tile records`)
|
|
if (missing.length > 0) {
|
|
console.error(`\nMISSING / UNREADABLE (${missing.length}):`)
|
|
for (const m of missing) console.error(` ${m}`)
|
|
}
|