UNPKG

pw-guild-icon-parser

Version:

Parser for Perfect World guild icon lists - converts PNG icons to DDS atlas format with DXT5 compression

83 lines 3.27 kB
import { readFile } from 'fs/promises'; import { decompressDXT5 } from './dxt5-decompress.js'; export async function readDDS(ddsPath) { const data = await readFile(ddsPath); if (data.length < 128) { throw new Error('DDS file too small to contain header'); } // Read magic number const magic = data.readUInt32LE(0); if (magic !== 0x20534444) { // "DDS " throw new Error('Invalid DDS magic number'); } // Read DDS header const header = readDDSHeader(data); // Validate DXT5 format const fourCC = header.pixelFormat.fourCC.trim(); if (fourCC !== 'DXT5') { // Show hex representation for debugging (FourCC is at offset 88) const fourCCBytes = data.subarray(88, 92); const hexStr = Array.from(fourCCBytes).map(b => '0x' + b.toString(16).padStart(2, '0')).join(' '); const asciiStr = fourCCBytes.toString('ascii').replace(/\0/g, ''); throw new Error(`Expected DXT5 format, got "${fourCC}" (bytes at offset 88: ${hexStr}, ASCII: "${asciiStr}")`); } // Extract compressed data const compressedData = data.subarray(128); // Decompress to RGBA const rgbaData = decompressDXT5(header.width, header.height, compressedData); return { header, width: header.width, height: header.height, rgbaData }; } function readDDSHeader(data) { const header = { magic: data.readUInt32LE(0), size: data.readUInt32LE(4), flags: data.readUInt32LE(8), height: data.readUInt32LE(12), width: data.readUInt32LE(16), pitchOrLinearSize: data.readUInt32LE(20), depth: data.readUInt32LE(24), mipMapCount: data.readUInt32LE(28), reserved: [], pixelFormat: readPixelFormat(data.subarray(72, 104)), // pixelFormat is 32 bytes, starts at offset 72 caps: data.readUInt32LE(108), caps2: data.readUInt32LE(112), caps3: data.readUInt32LE(116), caps4: data.readUInt32LE(120), reserved2: data.readUInt32LE(124) }; // Read reserved array (11 integers) for (let i = 0; i < 11; i++) { header.reserved.push(data.readUInt32LE(32 + i * 4)); } return header; } function readPixelFormat(data) { // DDS_PIXELFORMAT structure starts at offset 0 of this buffer // In the full DDS file, pixelFormat starts at offset 72 (after 4+4+4+4+4+4+4+4+44 = 76) // But this function receives a buffer starting at offset 72 // Read FourCC as 4 bytes (offset 8-11 in pixelFormat structure = offset 80-83 in full file) const fourCCBytes = data.subarray(8, 12); let fourCC = ''; for (let i = 0; i < 4; i++) { const byte = fourCCBytes[i]; if (byte !== 0 && byte >= 32 && byte <= 126) { // Printable ASCII fourCC += String.fromCharCode(byte); } } return { size: data.readUInt32LE(0), flags: data.readUInt32LE(4), fourCC: fourCC.trim() || fourCCBytes.toString('ascii').replace(/\0/g, ''), rgbBitCount: data.readUInt32LE(12), rBitMask: data.readUInt32LE(16), gBitMask: data.readUInt32LE(20), bBitMask: data.readUInt32LE(24), aBitMask: data.readUInt32LE(28) }; } //# sourceMappingURL=reader.js.map