/** Reconstruct the exact scalar/string semantics of positional archive rows. */ export function archiveRowsToDbc(records, fieldTypes) { if (!Array.isArray(fieldTypes) || fieldTypes.length !== 234) throw new Error("Archived Spell requires all 234 column types"); const strings = [Buffer.from([0])]; let stringSize = 1; const stringOffsets = new Map([["", 0]]); const rows = records.map((record) => { const row = Buffer.alloc(234 * 4); for (let index = 0; index < 234; index += 1) { const key = `f${index}`; if (!Object.hasOwn(record, key)) throw new Error(`Archived spell ${record.f0} is missing ${key}`); const value = record[key]; const localized = (index >= 136 && index <= 151) || (index >= 153 && index <= 168) || (index >= 170 && index <= 185) || (index >= 187 && index <= 202); const text = typeof value === "string" ? value : localized && typeof record[`${key}s`] === "string" ? record[`${key}s`] : null; if (text !== null) { if (!stringOffsets.has(text)) { const bytes = Buffer.from(`${text}\0`, "utf8"); stringOffsets.set(text, stringSize); strings.push(bytes); stringSize += bytes.length; } row.writeUInt32LE(stringOffsets.get(text), index * 4); } else if (fieldTypes[index] === "float") { if (!Number.isFinite(value)) throw new Error(`Archived spell ${record.f0} has invalid ${key}`); row.writeFloatLE(value, index * 4); } else { if (!Number.isInteger(value) || value < -2147483648 || value > 4294967295) throw new Error(`Archived spell ${record.f0} has invalid integer ${key}`); row.writeUInt32LE(value >>> 0, index * 4); } } return row; }); const header = Buffer.alloc(20); header.write("WDBC", 0); header.writeUInt32LE(rows.length, 4); header.writeUInt32LE(234, 8); header.writeUInt32LE(234 * 4, 12); header.writeUInt32LE(stringSize, 16); return Buffer.concat([header, ...rows, ...strings]); }