pw-guild-icon-parser
Version:
Parser for Perfect World guild icon lists - converts PNG icons to DDS atlas format with DXT5 compression
98 lines • 3.1 kB
JavaScript
import { writeFile } from 'fs/promises';
import { compressDXT5 } from './dxt5.js';
const DDS_MAGIC = 0x20534444; // "DDS "
export async function writeDDS(ddsPath, width, height, rgbaData) {
// Compress to DXT5
const compressedData = compressDXT5(width, height, rgbaData);
// Validate compressed data size
const blockWidth = Math.ceil(width / 4);
const blockHeight = Math.ceil(height / 4);
const expectedSize = blockWidth * blockHeight * 16; // 16 bytes per DXT5 block
if (compressedData.length !== expectedSize) {
throw new Error(`Invalid compressed data size: expected ${expectedSize}, got ${compressedData.length}`);
}
// Ensure linearSize fits in uint32
const linearSize = compressedData.length;
if (linearSize < 0 || linearSize > 0xFFFFFFFF) {
throw new Error(`Linear size out of range: ${linearSize}`);
}
// Create DDS header
const header = createDDSHeader(width, height, linearSize);
// Combine header and compressed data
const ddsFile = Buffer.concat([header, compressedData]);
await writeFile(ddsPath, ddsFile);
}
function createDDSHeader(width, height, linearSize) {
const header = Buffer.alloc(128);
let offset = 0;
// Magic number
header.writeUInt32LE(DDS_MAGIC, offset);
offset += 4;
// DDS_HEADER size
header.writeUInt32LE(124, offset);
offset += 4;
// Flags: DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT | DDSD_LINEARSIZE
header.writeUInt32LE(0x1 | 0x2 | 0x4 | 0x8 | 0x80000, offset);
offset += 4;
// Height
header.writeUInt32LE(height, offset);
offset += 4;
// Width
header.writeUInt32LE(width, offset);
offset += 4;
// PitchOrLinearSize
header.writeUInt32LE(linearSize, offset);
offset += 4;
// Depth
header.writeUInt32LE(0, offset);
offset += 4;
// MipMapCount
header.writeUInt32LE(1, offset);
offset += 4;
// Reserved (11 integers = 44 bytes)
offset += 44;
// DDS_PIXELFORMAT
// Size
header.writeUInt32LE(32, offset);
offset += 4;
// Flags: DDPF_FOURCC
header.writeUInt32LE(0x4, offset);
offset += 4;
// FourCC: "DXT5" (4 bytes ASCII)
const fourCC = Buffer.from('DXT5', 'ascii');
fourCC.copy(header, offset, 0, 4);
offset += 4;
// RGBBitCount
header.writeUInt32LE(0, offset);
offset += 4;
// RBitMask
header.writeUInt32LE(0, offset);
offset += 4;
// GBitMask
header.writeUInt32LE(0, offset);
offset += 4;
// BBitMask
header.writeUInt32LE(0, offset);
offset += 4;
// ABitMask
header.writeUInt32LE(0, offset);
offset += 4;
// DDS_HEADER continued
// Caps
header.writeUInt32LE(0x1000, offset); // DDSCAPS_TEXTURE
offset += 4;
// Caps2
header.writeUInt32LE(0, offset);
offset += 4;
// Caps3
header.writeUInt32LE(0, offset);
offset += 4;
// Caps4
header.writeUInt32LE(0, offset);
offset += 4;
// Reserved2
header.writeUInt32LE(0, offset);
offset += 4;
return header;
}
//# sourceMappingURL=writer.js.map