pw-guild-icon-parser
Version:
Parser for Perfect World guild icon lists - converts PNG icons to DDS atlas format with DXT5 compression
47 lines • 2.18 kB
JavaScript
import { open } from 'fs/promises';
import { compressDXT5Block } from './dxt5.js';
/**
* Patch a DDS file by updating only the blocks for a specific icon
* This is MUCH faster than decompressing/recompressing the entire atlas
*/
export async function patchDDSIcon(ddsPath, iconRgbaData, iconWidth, iconHeight, position, atlasWidth) {
// Calculate block positions
const blockWidth = Math.ceil(atlasWidth / 4);
// Position in pixels
const pixelX = position.col * iconWidth;
const pixelY = position.row * iconHeight;
// Position in blocks (DXT5 uses 4x4 pixel blocks)
const startBlockX = Math.floor(pixelX / 4);
const startBlockY = Math.floor(pixelY / 4);
// Number of blocks for this icon (16x16 = 4x4 blocks)
const iconBlocksX = Math.ceil(iconWidth / 4);
const iconBlocksY = Math.ceil(iconHeight / 4);
// DDS header is 128 bytes, then compressed data starts
const headerSize = 128;
const blockSize = 16; // DXT5 block is 16 bytes (4x4 pixels)
// Open file for random access
const fileHandle = await open(ddsPath, 'r+');
try {
for (let blockY = 0; blockY < iconBlocksY; blockY++) {
for (let blockX = 0; blockX < iconBlocksX; blockX++) {
// Calculate pixel position in the icon
const iconPixelX = blockX * 4;
const iconPixelY = blockY * 4;
const iconOffset = (iconPixelY * iconWidth + iconPixelX) * 4;
// Compress this 4x4 block
const compressedBlock = compressDXT5Block(iconRgbaData, iconOffset, iconWidth);
// Calculate position in the DDS file
const atlasBlockX = startBlockX + blockX;
const atlasBlockY = startBlockY + blockY;
const blockIndex = atlasBlockY * blockWidth + atlasBlockX;
const fileOffset = headerSize + (blockIndex * blockSize);
// Write the compressed block directly to file
await fileHandle.write(compressedBlock, 0, blockSize, fileOffset);
}
}
}
finally {
await fileHandle.close();
}
}
//# sourceMappingURL=patcher.js.map