Validate battle map WebP dimensions
This commit is contained in:
@@ -4,6 +4,8 @@ import { createServer } from 'vite';
|
||||
|
||||
const battleMapDir = join('src', 'assets', 'images', 'battle');
|
||||
const battleMapAssetsSourcePath = join('src', 'game', 'data', 'battleMapAssets.ts');
|
||||
const minimumRasterMapWidth = 1024;
|
||||
const minimumRasterMapHeight = 768;
|
||||
|
||||
const server = await createServer({
|
||||
logLevel: 'error',
|
||||
@@ -185,6 +187,18 @@ function validateMapFile(errors, context, path) {
|
||||
bytes.subarray(8, 12).toString('ascii') !== 'WEBP'
|
||||
) {
|
||||
errors.push(`${context}: "${path}" does not have a WebP RIFF header`);
|
||||
return;
|
||||
}
|
||||
|
||||
const dimensions = readWebpDimensions(bytes);
|
||||
if (!dimensions) {
|
||||
errors.push(`${context}: "${path}" does not expose readable WebP dimensions`);
|
||||
return;
|
||||
}
|
||||
if (dimensions.width < minimumRasterMapWidth || dimensions.height < minimumRasterMapHeight) {
|
||||
errors.push(
|
||||
`${context}: "${path}" is ${dimensions.width}x${dimensions.height}, expected at least ${minimumRasterMapWidth}x${minimumRasterMapHeight}`
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -192,6 +206,58 @@ function validateMapFile(errors, context, path) {
|
||||
errors.push(`${context}: "${path}" uses an unsupported map asset extension`);
|
||||
}
|
||||
|
||||
function readWebpDimensions(bytes) {
|
||||
let offset = 12;
|
||||
while (offset + 8 <= bytes.length) {
|
||||
const chunkType = bytes.subarray(offset, offset + 4).toString('ascii');
|
||||
const chunkSize = bytes.readUInt32LE(offset + 4);
|
||||
const dataOffset = offset + 8;
|
||||
if (dataOffset + chunkSize > bytes.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (chunkType === 'VP8X' && chunkSize >= 10) {
|
||||
return {
|
||||
width: readUInt24LE(bytes, dataOffset + 4) + 1,
|
||||
height: readUInt24LE(bytes, dataOffset + 7) + 1
|
||||
};
|
||||
}
|
||||
|
||||
if (chunkType === 'VP8L' && chunkSize >= 5 && bytes[dataOffset] === 0x2f) {
|
||||
const bits =
|
||||
bytes[dataOffset + 1] |
|
||||
(bytes[dataOffset + 2] << 8) |
|
||||
(bytes[dataOffset + 3] << 16) |
|
||||
(bytes[dataOffset + 4] << 24);
|
||||
return {
|
||||
width: (bits & 0x3fff) + 1,
|
||||
height: ((bits >>> 14) & 0x3fff) + 1
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
chunkType === 'VP8 ' &&
|
||||
chunkSize >= 10 &&
|
||||
bytes[dataOffset + 3] === 0x9d &&
|
||||
bytes[dataOffset + 4] === 0x01 &&
|
||||
bytes[dataOffset + 5] === 0x2a
|
||||
) {
|
||||
return {
|
||||
width: bytes.readUInt16LE(dataOffset + 6) & 0x3fff,
|
||||
height: bytes.readUInt16LE(dataOffset + 8) & 0x3fff
|
||||
};
|
||||
}
|
||||
|
||||
offset = dataOffset + chunkSize + (chunkSize % 2);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function readUInt24LE(bytes, offset) {
|
||||
return bytes[offset] | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16);
|
||||
}
|
||||
|
||||
function assetUrlToPath(url) {
|
||||
const marker = 'src/assets/images/battle/';
|
||||
const index = url.indexOf(marker);
|
||||
|
||||
Reference in New Issue
Block a user